MiddlemanでBootstrap-sassを使うとGlyphiconsが出ないことへの対処法
最近Middlemanにハマってます。
ここのページには、Bootstrap-sassを使うには単に
# Gemfile gem "bootstrap-sass", :require => false
で良いように書いていますが、これだけだとGlyphiconsが表示されません。
これに対処するには、config.rb中で以下のように読み込む必要があります。
# config.rb require "bootstrap-sass/sass_functions.rb"
尚、自分のブログでぶつくさ言ってるだけでContributeしないのは良くないので、Pull Request として送っておきました。
最近のWeb開発技術フロントエンド周りのまとめ(2014年)
主に知人向けに、最近のWeb開発技術について、私が最近知る限りの事をまとめておきます。
VagrantとかChefみたいに、おおまかに知っているだけで使ってないものも紹介しています。間違いがあったらコメントを頂ければ訂正しますので、ご指摘下さい。
また、フロントエンドよりの記述が多く、インフラやバックエンドのことには触れていません。
大前提
パッケージマネジメントシステムの有用性
Linuxのサーバーサイドでは当然、パッケージマネジメントシステムが存在しますが、ソフトウェア開発においてもこれは非常に重要になります。
- Java: sbt (Simple-Build-Tool)
- ruby: rubygems (gem)
- JavaScript: bower
などなど。
こういったシステムを使うことの利点として以下があります。
- ライブラリの公式の入手場所が辿りやすい
- どこからどこまでがライブラリか分かりやすい
- プロダクト特有のコードと、ライブラリのコードが分離される
- ライブラリのバージョンアップがしやすい
相互に重複気味な表現もありますがご容赦。
コンパイルする簡易言語たち
アセンブリ、C言語の歴史をWebが同じように辿っているだけなのですが。
Webブラウザはその基盤となる言語を変更しにくいです。(CPUの命令セットが変更しにくいのと同様) HTML、CSS、JavaScriptといったマークアップ言語およびプログラミング言語がそれにあたります。
Sass(SCSS) および LESS
SassとLESSは、CSSを書き出すための言語です。
私は数年前はLESSを使っていたのですが、 @extend が使える点などから、今ではSassに乗り換えています。
簡単にCSSが書けるだけであれば、大きな利便性はないのですが、@extend、@mixinなどの機能と、後述するCSSフレームワークとの相性が抜群です。
CoffeeScript
JSはプロトタイプベースだったり、this問題が起きたり、そもそも言語を問わず非同期処理は難しかったり、DOMが難しかったり、身につけるのが大変な言語ですが、CoffeeScriptは、それを少しだけ楽にしてくれます。少しだけです。
minifyとの相性
css,jsはminifyすることがありますが、セミコロンがないJSとかだと、minifyしたときにバグを生んでしまうことがあります。 上記の言語を使っていると、そういった問題が起きにくいように思います。
リソースをコンパイルするSprockets
Rails3.1に導入されたAsset pipeline(及びベースとなるSprockets)では、js,cssを簡単にコンパイルすることが出来る上、ハッシュ値を利用してキャッシュが有効にならないようにするなどの機能も有していて便利です。
また、上記のパッケージマネジャとの相性も考えられていて、パッケージマネジャで追加されたライブラリをJSやCSSの先頭で指定することで、それらを読み込むようにできます。(require, require_tree)
読み込んだファイルはSprocketsの設定にも依りますが、小規模なサイトであれば1つのJS,CSSに統合してしまうのが良いです。 そうすることで、コストの高い無駄なHTTPリクエストを減らすことができて、サイトの表示が非常に高速になります。
自動リロード
少し前から、自動リロードがアツいように思います。
検証する端末を全てデスクに広げておいて、コードを変更すると全てでリロードされる、なんてのも可能です。
livereload (guard-livereload)とか、Yeomanとか。
特に、HTML/CSSを記述するときはかなり手軽になって良いです。 マルチディスプレイとの相性が良いです。 表示を確認する画面、コードを書く画面、デザインファイルを参照する画面で3画面欲しいですね。 表示の確認はiPadとかのタブレットデバイスでも良いですけど。
ローカル環境の構築とデプロイ
デプロイ
デプロイは一般に面倒です。 ミスったときのロールバックはどうするのとか、変更されたファイルを選んでそれだけアップロードしたいなーとか、上記のコンパイルする言語を先にコンパイルしなきゃなーとか・・・。 で、その状況を放置して行くと、プロジェクトリーダーしかデプロイ方法が分からないといったことになって、リーダーが疲れているときに酷いミスをやらかして大変なことになるわけです。
これを防ぐために、デプロイの一連の処理はスクリプトとして記述するのが最近のトレンドです。
先のパッケージマネジャでライブラリをインストールするような処理もサーバー側で出来るので、直接転送する量が少なくて済む、なんてメリットもあります。
Rails界隈でのデプロイツールはCapistranoがデファクトスタンダードです。普通はgitと併用します。
CSSフレームワーク、JSフレームワーク
CSSもJSも、毎回似たようなことばかり書くのに辟易した偉い人達が、さまざまな知見を集約してくれました。昔とは大違いなので、CSSを1から書くとか、もうやめましょう。
CSSフレームワーク
特に有名なのがTwitter Bootstrapでしょう。 バージョン3ではHTML5対応、レスポンシブデザイン対応が行われ、クラス名も以前のrow-fluidといった面倒なのが減って、デザインも含めて以前より洗練されました。
バージョン3になってからは「これBootstrap臭がするな・・・」といった、特有の画面になることが減ったように思います。 トレンドがシンプルな(いわゆるフラット)デザインになっているのも大きいでしょうね。
にわかCSS原理主義者は、Bootstrapを使うとHTMLに直接classを定義するからセマンティックではないといったことを言うかもしれませんが、上記のsassを使うとそういった事はなくせます。自分が独自に定義したclassでextendすれば良いのですね。 尚「にわかCSS原理主義者」というのは過去の私です。。。
短いファイルでもsassのコンパイルに10秒以上かかるのがネックでしょうか。 でも、1から書くよりはやいことが多いと思います。
Middlemanでグローバルナビゲーションの今見ているページへのリンクにcurrentクラスをつける
Middleman便利ですね。
Webサイトといえばナビゲーションとかメニューが普通だとありますよね。 もちろんpartialなりlayoutなりに書いていると思うのですが、こうやって共通化してしまうと、一個だけ困る事があります。
まぁ、タイトルから推測がつくでしょう。 今みているページを示すナビゲーション要素を装飾したいときに困るんですよね。
こんな感じ。今Homeにいるから下にバーが出てる、という例。
ここで「しょうがない、メニューぜんぶ個別のページに書くか・・・」なんて考えるのはちょっと早い! 何とかするのが技術者、と思うわけです。
というわけで以下ハウツー。
本題
ヘルパーを追加する
まず、以下のヘルパーを追加しておきます。
"current_page?" はRailsなんかだとUrlHelperに含まれているのですが、Middlemanにはないので、適当に作ってしまいます。
# config.rb require "lib/helpers" helpers Helpers # lib/helpers.rb module Helpers def current_page?(path) current_page.url == path end def nav_link(link_text, link_path) class_name = current_page?(link_path) ? 'current' : '' content_tag(:li, :class => class_name) do link_to link_text, link_path end end end
directory_indexes をオンに
以下のコードも適当な位置に追加しておいてください。
#config.rb activate :directory_indexes
ナビゲーションのパーシャルを定義
僕はslim派ですが、別に何でも良いと思います。
/_nav.slim ul = nav_link "Home", '/' = nav_link "Profile", '/profile/' = nav_link "Works", '/works/' = nav_link "Links", '/links/' = nav_link "Contact", '/contact/'
ここで、先のcurrent_page? の実装が甘いので、最後のスラッシュが必要になります。
directory_indexes がオフな環境でどうするかは調べていないのですが、適当にcurrent_page.url なり current_page.path なりを見て検査すれば何とかなると思います。
viewDidUnloadが使えなくなった今、ViewControllerでどうやってNSNotificationをobserveするか
表題の通りですが、ViewControllerでNSNotificationを受け取る事は非常に典型的なパターンです。
ここでは2通りの想定パターンについて簡単に紹介します。 (以下のコードはARCを利用している事を前提としています)
画面が表示されてないときはNotificationを受け取らなくて良い場合
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeHoge:) name:AKNHogeDidChangeNotification object:nil]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [[NSNotificationCenter defaultCenter] removeObserver:self]; }
画面が表示されてないときにもNotificationを受け取りたい場合
こっちが本題です。
viewDidUnoadがあった頃は、viewDidUnloadとdeallocでやれば良かったのですが、そういうわけにはいかなくなりました。
また、アプリケーションの状態遷移によっては以下の様なケースがあるようで、これらに対処しなきゃなりません。
- ビューが最初にロードされる時にviewDidLoadが呼ばれる
- 他の画面に遷移
- メモリ警告発生(didReceiveMemoryWarningが呼ばれる)
- 元の画面に遷移
- viewDidLoadがまた呼ばれる
参考記事でも言及されているように、何も考慮せずにviewDidLoadでaddObserverしてdeallocでremoveObserverするだけだとNotificationを二回受け取る事になってしまうので、都合が悪いです。
なので、didReceiveMemoryWarningでremoveObserverしても良いんですけど、今後のiOSアップデートでちょっとでもイベントループが変化すると破綻するような感じで、あんまりやりたくない方針。
また、init系メソッドでaddObserverするという手もありますが、ビューのないビューコントローラでNotificationを受け取ったらクラッシュしそうだし、それを防ぐ制御コードを書くのも何だか馬鹿らしい。
などと考えた結果、以下の様にしちゃえば良いという事に気づきました。
- (void)viewDidLoad { [self addNotificationObserver]; } - (void)dealloc { [self removeNotificationObserver]; } - (void)addNotificationObserver { // 念のため先に削除する。 [self removeNotificationObserver]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeHoge:) name:AKNHogeDidChangeNotification object:nil]; } - (void)removeNotificationObserver { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
流石に、これが期待通りに動かなくなるようなイベントループの変更はこないでしょう。たぶん。。
Mac OSX mountain Lion でレインボーカーソルが出る問題とその対処(要約:Sophosをアンインストールしよう)
僕のMacBook Air 11-inch, Mid 2013 (OSX 10.9.1)での話ですが、多くのバージョンで同じ様な問題が発生するのではないかと。
経緯とか
最近どうもネットを見るときもワンテンポ遅れる。それだけならまだしも、レインボーカーソルが出てOSが反応しなくなってしまう事が増えたなぁと思っていて、
原因を調べてみると、bluetoothからのスリープ解除を無効にしろとかPRAMリセットしろとか、いろんな情報がひっかかります。
僕はbluetoothからのスリープ解除は使ってるし、それ以外の方法もあまり気乗りがしないなーと思いつつ、この状況にいつ頃からなったんだっけ、と考えていました。
そういえば、先日Macのセキュリティが不安になって無料のアンチウイルスソフト、Sophos Anti Virus を入れたんでした。
インストール直後からDNSをフックしてるのかブラウジングは重かったし、ちょうどその頃からだなーと思って
「Sophos rainbow cursor」などと検索してみると、こんな記事がヒット。
はい。どう見てもSophosが原因ですね。
Sophos Anti Virus のアンインストール方法
ちなみに、アンインストール手段が分かりにくいのですが、以下の手段で削除できるはずです。 (人によっては "Macintosh HD"が違う名前になってる事もあります。)
ここでは現時点(2014年2月6日)で最新のSophosのバージョン9系を想定しています。
- 「ターミナル.app」を開く
- cd "/Volumes/Macintosh HD/Library/Sophos Anti-Virus" と入力してEnter
- sudo ./remove_v9.sh
- パスワードを聞かれるので入力してEnter
- 5秒ぐらい待つと削除処理が動くので、止まったのを確認してからターミナルを閉じる
今のところおすすめはESET Cyver Securityかなぁ。
さて、削除も済んだところで、軽さに定評のある僕はESET Cyber Securityを導入しました。まだ体験版ですが、このまま何事もなければ購入すると思います。
Kasperskyとも迷ったんですが、ずいぶん昔に未知のウイルスをヒューリスティクスで検出してて印象よかったし、評判も良かったのでESETにしてみました。
別に他の製品でも良いと思いますけれど、どうしても有償ソフトの方が信頼出来ますね。以外と安いし。
- 価格.com改竄事件、NOD32だけがウイルスを検知したのはなぜか (NOD32の頃の2005年の記事ですし、今は殆ど別物かもしれませんけどね。こっちはMac版だし。)
Amazonでも買えるみたいです。
- 出版社/メーカー: キヤノンITソリューションズ
- 発売日: 2013/12/12
- メディア: CD-ROM
- この商品を含むブログを見る
※ あまり無責任な事を言うとSophosの中の人からおこられるなーと思ったので、ESETに変えてから数日たってから記事を公開しています。 残念ながらSophosアンチウイルスが原因である事は明白でした。こういうソフトを作るのは大変ですね。
iPhone5S(arm64)でBlocksKitが使えるようになってた
libffiのバグが原因で、arm64向けにビルドしているとBlocksKitが使えませんでしたが、つい5日前ぐらいに直ったようです。
これは以前のBlocksKit1系ではなく、2系に対して行われています。
詳しく調べていないのですが、2系で廃止されたメソッドがある場合には対処できないかもしれません。 その場合は、速度を諦めてarmv7のみでビルドするなどで対処する方が良さそうです。
ただ、少し見た限り、2系はメソッドが洗練されていて、1系より良くなっているようでした。
現時点で対応するための Podfile の書き方
とりあえず以下のように書いておけばOKです。 そのうちstableとしてリリースされるでしょう。
pod 'BlocksKit', :git => 'https://github.com/pandamonia/BlocksKit.git', :commit => 'aa99a68674'
Issueに記述されている以下の指定方法だと、時折nextブランチにバグが含まれたりして、頻繁にバグを含んでしまいます。(実際ひっかかった…) まぁリリース前なのでしょうがないですね。
pod 'BlocksKit', :git => 'https://github.com/pandamonia/BlocksKit.git', :branch => 'next'
追記
@AknEp https://t.co/dXWgZGhd3w
— Seivan Heidari (@Seivanheidari) 2014, 1月 14
こんな感じで紹介してもらったのですが、libffiを使わずに似たような機能を提供してくれるライブラリもあるみたいです。かなりしっかり作られているようなので、こちらの導入も検討してみたいですね。
iOSアプリ開発における正しいMVC
この記事は iPhone Advent Calendar 2013 - Adventar の12月24日分の記事になります。
今年もアドベントカレンダーが熱いわけですが、今年は僕は年中iOSアプリのコードを書いていたので、その中で手に入れた or 手に入れつつある様々な知見を皆さんに紹介していきたいと思います。
MVCって何だっけ? 〜UITableViewCellってあれで良いのか?〜
この記事を読んでいる方で、こんなコードを書いている人はいませんか?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; UIImageView *myImageView = (UIImageView *)[cell viewWithTag:10]; myImageView.image = [UIImage imageNamed:self.imageName]; return cell; }
はい。少し前までの僕です。。
しかし、ここはあえて言わせて頂きたい。 糞コードだ! …と。
そもそもこれって、Viewの分離ができていないわけで、"M"と"VC"でしか分かれてないわけです。 それってPHPでSymfony使って書いてるのと変わらない概念なわけで、そんな前時代的な(以下省略
正しいUITableViewCellの扱い方
こんな時、どうすればいいのかっていうと、最低限やりたいのが「Viewの分離」なわけです。Controllerのもつ役割とViewの持つ役割を分離しましょう。
具体的には、こんな感じ。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; AKNGoodTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; AKNEntry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.titleLabel.text = entry.title; cell.bodyLabel.text = entry.body; return cell; } // 途中省略 @interface AKNGoodTableViewCell : UITableViewCell @property (nonatomic) IBOutlet UILabel *titleLabel; @property (nonatomic) IBOutlet UILabel *bodyLabel; @end @implementation AKNGoodTableViewCell @end
こんなふうにして、StoryBoardからアウトレットを引っ張ってあげましょう。 ちゃんとAutomatic の所に出てきますよ。
と、ここまで書いたのですが、コードで説明したほうが早いのでコードを書いていたら深夜になったので、一旦この記事を公開してしまいます。
後で説明を追記しようと思うので、良かったら左上、「読者になる」ボタンを押してやって下さい!
その他ありそうな疑問など
これじゃCellを複数のViewControllerで使いまわせないけどどうしたらいいの?
セルに対応したNibファイルを作っておいて、 registerNib:forCellReuseIdentifier: というメソッドを使うと解決します。
但し、UITableViewControllerで直接的に行うのではなく、対応するセルのクラス側にクラスメソッドを容易すると、よりエレガントになりそうです。 (ファイル名を再定義する必要がなくなるので。)
これって普通のViewController でも同じようなことをした方が良いの?
yesです。
特に、複雑な画面では、部分ごとにUIViewのサブクラスを作ってあげると、効果的でしょうね。
フリーランスでやってます。
いろいろ引き受けてます。興味があったら以下のメールアドレスまでどうぞ。 ご相談お待ちしております。
master(at-mark)silph.jp