PaintCodeでiOSアプリ用ボタンを作ってみた

PaintCodeを買ってみたので、早速iOSアプリ用ボタンを作ってみました。

直感的に使用できるし、ボタン作成には丁度いいアプリでした。PDFで書き出すとAdobe Illustratorでレイヤーが保存された状態で利用できるので、効果をつけたりしたい場合はイラレに持っていくこともできます(それなら最初からイラレで作るって?)。やはりコードが出力されるのが魅力かな。

起動するとWindowが表示されます。
120319 0003

カンバスサイズを90×40に変更します。OS X/iOSのボタンをiOSに変更します。コードがiOS向けになります。原点表示も左上になります。
120319 0005

角丸長方形を描画。ストローク無しです。
120319 0006

#222222/#444444/#666666の色設定を追加。Hex Color Pickerで指定しました。
120319 0007

グラーションを追加。一番左が黒、左から2番目が#222222、3番目が#444444、一番右が#666666。Fill Styleに作成したグラデーションを指定。
120319 0009

Inner Shadowを作成して指定。
120319 0011

文字列を追加。
120319 0012

テキストにOuter Shadowを指定。
120319 0013

でき上がったコードがこれ。

//// General Declarations
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
 
//// Color Declarations
UIColor* color222222 = [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: 1];
UIColor* color444444 = [UIColor colorWithRed: 0.27 green: 0.27 blue: 0.27 alpha: 1];
UIColor* color666666 = [UIColor colorWithRed: 0.4 green: 0.4 blue: 0.4 alpha: 1];
 
//// Gradient Declarations
NSArray* gradientColors = [NSArray arrayWithObjects: 
    (id)[UIColor blackColor].CGColor, 
    (id)[UIColor colorWithRed: 0 green: 0 blue: 0 alpha: 1].CGColor, 
    (id)color222222.CGColor, 
    (id)color444444.CGColor, 
    (id)[UIColor colorWithRed: 0.33 green: 0.33 blue: 0.33 alpha: 1].CGColor, 
    (id)color666666.CGColor, nil];
CGFloat gradientLocations[] = {0, 0.25, 0.45, 0.51, 0.75, 1};
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)gradientColors, gradientLocations);
 
//// Shadow Declarations
CGColorRef innerShadow = color444444.CGColor;
CGSize innerShadowOffset = CGSizeMake(0, -0);
CGFloat innerShadowBlurRadius = 2;
CGColorRef textShadow = [UIColor blackColor].CGColor;
CGSize textShadowOffset = CGSizeMake(0, -0);
CGFloat textShadowBlurRadius = 3;
 
//// Abstracted Graphic Attributes
NSString* textContent = @"Touch!";
UIFont* textFont = [UIFont fontWithName: @"Helvetica-Bold" size: 18];
 
 
//// Rounded Rectangle Drawing
UIBezierPath* roundedRectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, 90, 40) cornerRadius: 4];
CGContextSaveGState(context);
[roundedRectanglePath addClip];
CGContextDrawLinearGradient(context, gradient, CGPointMake(45, 40), CGPointMake(45, -0), 0);
CGContextRestoreGState(context);
 
////// Rounded Rectangle Inner Shadow
CGRect roundedRectangleBorderRect = CGRectInset([roundedRectanglePath bounds], -innerShadowBlurRadius, -innerShadowBlurRadius);
roundedRectangleBorderRect = CGRectOffset(roundedRectangleBorderRect, -innerShadowOffset.width, -innerShadowOffset.height);
roundedRectangleBorderRect = CGRectInset(CGRectUnion(roundedRectangleBorderRect, [roundedRectanglePath bounds]), -1, -1);
 
UIBezierPath* roundedRectangleNegativePath = [UIBezierPath bezierPathWithRect: roundedRectangleBorderRect];
[roundedRectangleNegativePath appendPath: roundedRectanglePath];
roundedRectangleNegativePath.usesEvenOddFillRule = YES;
 
CGContextSaveGState(context);
{
    CGFloat xOffset = innerShadowOffset.width + round(roundedRectangleBorderRect.size.width);
    CGFloat yOffset = innerShadowOffset.height;
    CGContextSetShadowWithColor(context,
        CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
        innerShadowBlurRadius,
        innerShadow);
 
    [roundedRectanglePath addClip];
    CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(roundedRectangleBorderRect.size.width), 0);
    [roundedRectangleNegativePath applyTransform: transform];
    [[UIColor grayColor] setFill];
    [roundedRectangleNegativePath fill];
}
CGContextRestoreGState(context);
 
 
 
 
//// Text Drawing
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, textShadowOffset, textShadowBlurRadius, textShadow);
CGRect textFrame = CGRectMake(5, 8, 80, 20);
[[UIColor whiteColor] setFill];
[textContent drawInRect: textFrame withFont: textFont lineBreakMode: UILineBreakModeWordWrap alignment: UITextAlignmentCenter];
CGContextRestoreGState(context);
 
 
//// Cleanup
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
Log

node.js/npm/expressのインストール with Homebrew

node.js/npm/expressをHomebrewを使いつつインストール。

$ brew install node.js
$ curl http://npmjs.org/install.sh | sh
$ npm install express
$ ln -s /usr/local/node_modules/express/bin/express /usr/local/bin/express
Log

FuelPHPでデフォルトタイムゾーンを設定する

FuelPHPでデフォルトタイムゾーンを設定するにはfuel/app/config/config.phpで

'default_timezone'   => 'Asia/Tokyo'

のようにタイムゾーン名で設定する。

Log

FuelPHPで管理画面を作ってみる

今日はFuelPHPを触ってみる。

2年以上前にKohanaをつかってWebサービス作ったり、Drupalのモジュール書いたりしてた。で、久しぶりにPHPでサーバアプリを作成することになった。で、どのフレームワークを使うか検討してみたんだけど、KohanaよりFuelPHPの方がよさそう。なので、FuelPHPを触って、ついでにPHPのリハビリ。

本当はRuby on Railsでやりたいんだけどね…

早速MAMPの更新。ここんところ使ってなかったので、バージョンがちょっと上がってた。

FuelPHPのインストール

$ curl get.fuelphp.com/oil | sh

oilという実行ファイルをインストール。oilを使ってプロジェクトを作成するらしい。oilは/usr/bin/oilにインストールされる。

プロジェクトを作成

$ oil create MyToDo

と実行すると、プロジェクトに必要なファイルがダウンロードされ、プロジェクトが作成される。こんなディレクトリ構成。

.
├── docs
│   ├── assets
│   │   ├── css
│   │   ├── fonts
│   │   ├── img
│   │   └── js
│   ├── classes
│   │   ├── agent
│   │   ├── cache
│   │   ├── database
│   │   ├── file
│   │   ├── model_crud
│   │   ├── mongo
│   │   ├── session
│   │   ├── upload
│   │   └── validation
│   ├── general
│   │   └── controllers
│   ├── installation
│   ├── packages
│   │   ├── auth
│   │   ├── email
│   │   ├── oil
│   │   ├── orm
│   │   └── parser
│   └── templates
├── fuel
│   ├── app
│   │   ├── cache
│   │   ├── classes
│   │   ├── config
│   │   ├── lang
│   │   ├── logs
│   │   ├── migrations
│   │   ├── modules
│   │   ├── tasks
│   │   ├── tmp
│   │   ├── vendor
│   │   └── views
│   ├── core
│   │   ├── classes
│   │   ├── config
│   │   ├── lang
│   │   ├── tasks
│   │   ├── tests
│   │   ├── vendor
│   │   └── views
│   └── packages
│       ├── auth
│       ├── email
│       ├── oil
│       ├── orm
│       └── parser
└── public
    └── assets
        ├── css
        ├── img
        └── js

CodeIgniter系なのでKohanaとほぼ同じ(だっけ?)。

MAMPで表示するために

$ ln -s /Users/username/Documents/Workspace/MyToDo/public /Applications/MAMP/htdocs/mytodo

とシンボリックリンクを張る。MAMP(ポートを標準設定にしている)を起動して、http://localhost/mytodoにアクセスするとWelcomeページが表示された。

FuelPHP Welcomeページ

設定

まずDBの設定を行う。
MAMPのphpmyadminでデータベース「mytodo」を作成する。
fuel/app/config/development/db.phpを編集する。
dbname=mytodoとする。

return array(
	'default' => array(
		'connection'  => array(
			'dsn'        => 'mysql:host=localhost;dbname=mytodo',
			'username'   => 'root',
			'password'   => 'root',
		),
	),
);

次にORMを利用するように設定する。fuel/app/config/config.phpを編集する。packagesでormがコメントアウトされているので有効にする。認証も行うので、authも入れる。

	/**************************************************************************/
	/* Always Load                                                            */
	/**************************************************************************/
	'always_load'  => array(
 
		'packages'  => array(
		    'auth',
		    'orm',
		),

また、同ファイルを以下のように変更する。

		'whitelisted_classes' => array(
			'Fuel\\Core\\Response',
			'Fuel\\Core\\View',
			'Fuel\\Core\\ViewModel',
            'Fuel\Core\Validation',
			'Closure',
		)

続いて、認証におけるグループを設定する。fuel/packages/auth/config/simpleauth.phpを編集する。

	'groups' => array(
		-1   => array('name' => 'Banned', 'roles' => array('banned')),
		0    => array('name' => 'Guests', 'roles' => array()),
		1    => array('name' => 'Users', 'roles' => array('user')),
		50   => array('name' => 'Moderators', 'roles' => array('user', 'moderator')),
		100  => array('name' => 'Administrators', 'roles' => array('user', 'moderator', 'admin')),
	),

同ファイルを編集してゲストログインができないように設定する。

	'guest_login' => false,

ユーザの作成

ユーザモデルを作成する。

$ oil generate model users username:varchar[50] password:string group:int email:string last_login:int login_hash:string profile_fields:text
$ oil refine migrate

oilのコンソールモードでadminユーザを作成する。

$ oil console
Fuel 1.1 - PHP 5.3.6 (cli) (Sep 15 2011 11:18:57) [Darwin]
>>> Auth::create_user('admin', 'password', 'r.izumita@example.com', 100);
Warning - Cannot modify header information - headers already sent by (output started at /Users/rizumita/Documents/Workspace/MyToDo/fuel/core/classes/cli.php:127) in COREPATH/classes/cookie.php on line 95
1
>>> [ctrl-c]

コードの生成

oil genetare adminコマンドで認証付きのコードが生成される。ToDoモデルとToDoテーブル、コントローラ、ビューが作成される。

$ oil generate admin todos title:string summary:text due:datetime user_id:int
$ oil refine migrate

ブラウザでadmin/todosにアクセスすると認証を求められる。
Login
上で設定したadmin/passwordでログインする。と、Dashboardが表示される。
Dashboard
ナビゲーションバーにTodosがあるのでクリックするとToDoの一覧ページが表示される。
Todos
Add new TodoボタンからToDo追加画面に遷移する。

とりあえず管理画面までできた。
ユーザの選択とか日付の入力のカスタマイズはおいておく。

Storyboardで起動時にNavigation Controllerのビューコントローラを構成する

Storyboardを利用している時に、起動時にNavigation Controllerの子ビューコントローラの構成を変更してみます。

まずStoryboardを以下のように作成します。
NewImage

Initial view controllerをUINavigationController、FirstViewControllerをUINavigationControllerに接続、SecondViewControllerを別個作成しています。
SecondViewControllerには以下のようにIdentifierにSecondViewControllerを指定しています。
Identifier

次にApplication Delegateの
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
メソッドを以下のように記述します。

UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIViewController *secondViewController = [navigationController.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
[navigationController pushViewController:secondViewController animated:NO];
return YES;

StoryboardのInitial view controllerであるUINavigationControllerがwindowのrootViewControllerにセットされているので、それを取り出します。コントローラからStoryboardを取り出し、instantiateViewControllerWithIdentifierメソッドで指定したコントローラをインスタンス化します。上記コードではSecondViewControllerをインスタンス化しています。そのSecondViewControllerをUINavigationControllerにpushして、表示しています。

アプリを実行すると

second_view.png
このように、SecondViewControllerのビューが表示されます。

このようにinstantiateViewControllerWithIdentifierでインスタンス化することで、Segueで指定していないビューコントローラを利用することができます。

カスタムStoryboardセグウェイを作成する

Storyboardではカスタマイズしたセグウェイを利用することができます。

UINavigationControllerでフリップで画面を切り替えるセグウェイを作ってみます。

まず、UIStoryboardSegueを継承したFlipSegueクラスを作成します。
次に、FlipSegueクラスに以下のコードを追加します。

- (void)perform {
    UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;
    UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;
    [UIView transitionWithView:sourceViewController.navigationController.view
        duration:0.2
        options:UIViewAnimationOptionTransitionFlipFromLeft
        animations:^{
            [sourceViewController.navigationController pushViewController:destinationViewController animated:NO];
        }
        completion:nil];
}

ソースビューからデスティネーションビューにフリップアニメーションを行いつつ、Navigation Controllerでコントローラの切り替えをアニメーション無しで行っています。
そして、StoryboardでSegueをCustomに変更してFlipSegueを指定します。
NewImage

Everword 1.1を公開しました

Everword バージョン1.1を公開しました。

主な変更は共有ノートブックのサポートです。アプリ内課金時のみ共有ノートブックを利用できます。

Log

XcodeでARCに変換できなかった時

XcodeでConvert to Objective-C ARCでARC化しようとした時に、Saveボタンを押しても変換されなかった。

スナップショットを作成できないのが問題らしく、調べたら.gitが邪魔らしい。.gitを外部に移動させるといいという情報にしたがって、.gitを移動して再度実行してみた。

が、失敗。Saveできない。

さらに検索すると、Developer Toolsをアンインストールして再度インストールするといいという情報が。試してみたが、やはりうまくいかない。

どうしようもなくなったが、ふと思い立って、試しにプロジェクトフォルダを他の場所に移動(この時はデスクトップ)に移動して、.gitを外部に出して、再度Saveしてみると成功。

何が悪かったか分からないが、とにかく変換できた。

Log

Xcodeで.hファイルがfile not foundになったら

Xcodeで.hファイルをimportすると、File not foundになって、でもビルドは出来る状態になることがある。
その場合は、当該ターゲットを表示して、Build PhasesのCompile Sourcesからその.hファイルの対になる.mファイルを削除して、再度追加すると直る。

Log

CALayerの3D transformが変換画像に反映されなくてあせった

今請け負っている仕事でViewを画像変換して保存するという仕様があるのだが、CALayerを3d transformしていて、いざrenderInContextで画像に変換しようとしたら、transformが反映されませんでした。ドキュメント見たら「layers that use 3D transforms are not rendered」なんて書かれてて、あと二日くらいでOpenGLで書き直しか?と非常にあせった。

で、3Dな変換は行っていなかったので、かわりにaffineTransformを使ってみたら無事変換できた。

Get Adobe Flash playerPlugin by wpburn.com wordpress themes