Codeから遷移したUIViewControllerにstoryboard上でナビゲーションバーを表示する
storyboad上のSegueで画面遷移をつないでいった場合、UINavigationControllerに含まれるViewControllerは自動でナビゲーションバーが表示されて、そこにタイトルとかバーボタンをとかを配置することができるのだけど、コードから遷移させた場合storyboad上ではどことも繋がっていないViewControllerとして表現されてしまって、実際はUINavigationControllerの中にいてナビゲーションバーとかがあるのにそれが表示されていない、ということが起こる。
そんなViewControllerの場合以下のようにしてやればstoryboard上にナビゲーションバーを表示でき、IB上でタイトルやボタンを配置することができます。
- storyboard上でViewControllerを選択する。
- Attributes inspectorで
Simulated Metrics
設定のTop Bar
をTranslucent Navigation Bar
に設定する - storyboard上にナビゲーションバーの領域があらわれる
- ナビゲーションバーの領域に
Navigation Item
をドラッグアンドドロップして配置する
以上。
Cocoapodsにあげたライブラリをアップデートする
毎度公式ドキュメントを参照していたのでメモ。以下のコマンドを叩くだけ
pod trunk push NAME.podspec
UILabelを上寄せにする
よく忘れるので、メモ
- AutoLayoutでheight以外のconstraintをつける
- IBでPreferred Widthを0
- IBでLinesを0、または表示させたい最大の行数
Preferred Widthが何の設定なのか調べてない。。。
UITextFieldやUITextViewでキーボードの外をタップしたらキーボードを閉じる
すでにいろいろやり方がネット上に書かれているが、自分の中では以下の方法に落ち着いたのでメモっておく。
まず、以下の記事で紹介されているようなFirstResponderを取得するメソッドをUIViewにカテゴリとして事前に実装しておく。
#import "UIView+UIUtil.h" @implementation UIView (UIUtil) - (UIView *)findFirstResponder { if ([self isFirstResponder]) { return self; } for (UIView *subView in [self subviews]) { if ([subView isFirstResponder]) { return subView; } if ([subView findFirstResponder]) { return [subView findFirstResponder]; } } return nil; } @end
あとはViewController上で以下のように実装する。
# XXHogeViewController.m - (void)viewDidLoad { [super viewDidLoad]; // タップを検知するためのGestureRecognizer。デリゲートで処理するように設定 UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] init]; gestureRecognizer.delegate = self; gestureRecognizer.cancelsTouchesInView = NO; [self.view addGestureRecognizer:gestureRecognizer]; } // タップ時のデリゲートメソッド - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { UIView *touchedView = touch.view; UIView *firstResponderView = [self.view findFirstResponder]; if (touchedView != firstResponderView) { // 現在編集中のViewと違う場所がタップされたときのみキーボードを閉じる [self.view endEditing:YES]; } return YES; }
PHPでcronのように定期実行をするプログラムを書いた
kohkimakimoto/workerphp · GitHub
自分で使う分には最低限、必要な機能を実装できたので紹介します。以下のようなPHPファイルを記述してPHPのコマンドラインから実行するとプロセスが起動しっぱなしになりcront_time
で指定したスケジュールでジョブを定期実行してくれます。
<?php require_once __DIR__.'/vendor/autoload.php'; $worker = new \Kohkimakimoto\Worker\Worker(); // job for every minute. $worker->job("hello", ['cron_time' => '* * * * *', 'command' => function(){ echo "Hello world\n"; }]); // job runs a shell command. $worker->job("uptime", ['cron_time' => '10 * * * *', 'command' => "uptime"]); $worker->start();
ジョブはクロージャでPHPのコードとして書くか、シェルのコマンドを文字列で直接指定することができます。インストールはcomposer install
するだけですが、その他詳しい情報はリポジトリのREADMEを参照してください。内部的にジョブはプロセスをforkして実行させているので、PHPのpcntl
エクステンションがインストールされている必要があります。linuxならyumとかでインストールできると思います。
cronが普通に使える環境ならまあそれを使えばいいんですが、個人の用途でherokuの無料枠dynoを使って定期実行処理をしたかったので、こんなの作りました。
Web API
cronにない拡張機能として、簡易なWeb APIを提供するHTTPサーバを組み込んでいます。
$worker->httpServer->listen();
のように書くと、8080ポートを開きます。ポートを変更したいときは
$worker->httpServer->listen(8888, 'localhost');
のようにもかけます。HTTPサーバが立ち上がったら、
$ curl -XGET http://localhost:8080/{ジョブ名}
でジョブの情報を取得
$ curl -XPOST http://localhost:8080/{ジョブ名}
でジョブを実行することができます。また$worker->httpServer->setAPIKey()
を使えば、文字列一致だけで判定する簡易な認証機能をつけられます。
ちなみに、私は以下の様な設定でherokuのPHPスタック上で動かしています。
<?php require_once __DIR__.'/vendor/autoload.php'; date_default_timezone_set('Asia/Tokyo'); use Kohkimakimoto\Worker\Worker; $worker = new Worker(); // herokuはhttpのportを動的に割り当てるので環境変数から取得する $worker->httpServer->listen(getenv('PORT')); // apiにアクセスするときのキー $worker->httpServer->setAPIKey('vjdioaewdg49q3tg...'); // 5分毎にメモリ使用量などをログに出力する設定 $worker->stats->on(); $worker->job("hoge", ['cron_time' => '* * * * *', 'max_processes' => 1, 'command' => function(){ // ... }]); $worker->job("foo", ['cron_time' => '* * * * *', 'max_processes' => 1, 'command' => function(){ // ... }]); // 30分に一回dynoをwebからアクセスしてスリープさせないようにする。 $worker->job("wakeup_dyno", ['cron_time' => '*/30 * * * *', 'max_processes' => 1, 'command' => function(){ file_get_contents("https://my_worker_xxx.herokuapp.com/?apiKey=vjdioaewdg49q3tg..."); }]); $worker->start();
とりあえず、1日動かしっぱなしにしてみたが問題はなさそうです。
UIPageViewControllerをUINavigationControllerにいれたらナビゲーションバー部分に潜り込まなくなった件
iOS8でUIPageViewController
のトランジションスタイルをUIPageViewControllerTransitionStyleScroll
にしたときのみ発生する。。。マジなんなのこれ。。。しかも一度でも画面をタップすると、潜り込む(正常な)位置に移動するという。挙動が奇妙すぎてStack Overflowでも見つからないし。iOS8のバグを疑ってしまう。ヾ(`Д´)ノ"
このサンプルはXcodeのPage-Based Application
が作った雛形のプロジェクトを元に、RootViewController
をNavigationControllerにいれる、のとUIPageViewController
の初期化処理を以下のようにUIPageViewControllerTransitionStyleScroll
を指定するように変えたもの。
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
表示されるViewのframeなどを調べてみてもx=0,y=0となっていて変なところはない。それでViewの階層を見てみたらこんな感じになっている。 なんか下にズレてる。。。
_UIQueuingScrollView
というViewの位置がy=-64.0
という謎な状態になっていて、その子となるViewがその分だけ下にずれている。
しかもこれviewWillAppear:
などのフェーズでは確認できず、viewDidAppear:
まで到達して初めてこの状態になる。
というわけで以下のようなワークアラウンドを入れた。
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)) { for (UIView *v in self.pageViewController.view.subviews){ if ([v isKindOfClass:[UIScrollView class]]) { UIScrollView *scv = (UIScrollView *)v; CGPoint offset = scv.contentOffset; if (offset.y < 0) { offset.y = 0; scv.contentOffset = offset; } } } } }
iOS8の場合、UIPageViewController
のUIScrollView
をとってきてマイナス値なら0に書き換えるということをやっています。これでなんとか対応できた。
追記
コメント欄でアドバイスをいただいて「UIPageViewControllerのAdjust Scroll View Insetsのチェックを外す」で対応できることがわかりました。
Goの開発環境
最近Go言語をいじっているので、現時点での開発環境についてまとめておく。ツールを先に列挙しておくと以下のものを使っている。
エディタ
Sublime Text3を使っている。普段PHPを書くのに使っていて慣れているからであって最適だとは思ってはいない(静的型言語はIDEを使う方がいいとは思っているのだが、ショートカットキーを新たに覚えたりするのがつらいので使い続けている)。
これにGo用のプラグインのGoSublimeをインストールしている。
また関数定義などにジャンプするためにctagsも使っている。ctags自体はhomebrewでインストールして、SublimeにはCtagsプラグインを入れている。標準だとctagsはGoに対応していないのだが
go - ctag database for golang - Stack Overflow
にあるように、~/.ctags
に
--langdef=Go --langmap=Go:.go --regex-Go=/func([ \t]+\([^)]+\))?[ \t]+([a-zA-Z0-9_]+)/\2/d,func/ --regex-Go=/var[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,var/ --regex-Go=/type[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,type/
と書き込んでおけば、関数定義元に(ある程度)ジャンプできるようなる。完璧にとはいかないが、今のところはこれでいいかと。
GOPATHとディレクトリ構造
ghqを使ったローカルリポジトリの統一的・効率的な管理について - delirious thoughts
基本は上記の記事の通り。GOPATH=$HOME
に設定してghqでGo以外のリポジトリも含めて~/src
配下で一括管理する。
ただ、これだとctagsでタグを生成する際に全てのリポジトリを対象にしなくてはならず、タグ生成処理がものすごく重くなってしまい使いものにならなかった。やはり依存パッケージは個々のリポジトリ配下にほしい。
いろいろ試行錯誤した後、次のようにdirenvを使う方法に落ちついた。direnvはカレントディレクトリごとに環境変数を自動で切り替えてくれるツールで、以下の記事が参考になる。
まず
brew install direnv
でdirenv
をインストールし、zshの場合は~/.zshrc
に以下を記述しておく。
export EDITOR=vim eval "$(direnv hook zsh)"
さて、ホームのsrcディレクトリは以下のようになっている。
src ├── github.com │ ├── laravel │ │ ├── framework │ │ └── laravel │ ├── kohkimakimoto │ │ └── altax │ ├── php │ │ └── php-src │ └── piranha │ └── gostatic
ここでGo言語の場合はリポジトリのルートに.envrc
を作成して
export GOPATH=$(pwd)/_vendor/:$GOPATH
と書いておく。これでGo言語のリポジトリに移動した時のみGOPATHがそのリポジトリ配下の_vendor
ディレクトリにも設定される。go get
で外部パッケージをインストールするとき_vendoer
配下にインストールされるようになるし、go build
時もここのパッケージを参照するようになる。ctagsのタグ生成もリポジトリルートで行えば依存のあるパッケージをだけを対象にタグを生成できる。
参考にしたサイトは以下。
ghqによるGoのプロジェクト管理 & プロジェクト毎で外部モジュール管理 - Qiita
これからGoを始める人のためのTips集 #golang - The Wacul Blog
Gomで依存パッケージを一括で_vendorにダウンロード
_vendor
に依存パッケージを配置できるようになったのはいいのだが、依存物をいちいち手作業でgo get
するのは非効率なので一括でダウンロードできるようにGo言語版のBundlerなやつであるGomを使っている。
以下のコマンドを叩くとgo build
を実行してインストールされていないパッケージをエラーメッセージから抽出してGomfile
に吐き出す。
go build 2>&1 | awk '/cannot find package/{print "gom " $5}' >> Gomfile
これでGomfile
ができたら
gom install
として全ての依存パッケージを_vendor
配下にダウンロードする。これでGoのプロジェクトがビルドできる\(^o^)/