オートレイアウト利用でも自動でストーリーボード上の全UIScrollViewをスクロールできるようにするハック

# 重要な追記
追記なのですが重要なことなので冒頭に書かせていただきます。
この記事で紹介しているやり方には欠陥がありました。
他の方が書いている優秀な記事 http://goo.gl/GPdBhu があったのでこちらを参考にしてください。
この記事のやり方はiOS7系のiPhone5,5s,実機,シミュとiOS8のiPhone4,4s,5,5sシミュで動作するのですが、なぜかiOS7の4,4s（つまり3.5インチ画面）でのみ正常に動作しません。（contentSizeの変更がviewDidLayoutSubviewsで変更されているにも関わらず）
ですので、以下を読む場合はコードインジェクションを活用できる場面の参考にする程度（記事の趣旨と全く異なりますが）にしてください。

# 面倒ごとが嫌いな開発者たちへ贈るハック
UIScrollViewのcontentSizeの設定、超面倒だ。でもこれやらないとスクロールできないぜ。せっかくならストーリーボードだけでさらっと完結したいぜ。
スクロールビューをコードに紐づけて、__weak IBOutlet UIScrollView*なんてやってたらUIとコードの依存度が高まるだけでいいことナッシー。
じゃあどうしようよ。

# 以前までは俺なりのハックがあった
オートレイアウトが必要となるまではこんなコードを書いてたんだぜ。

```objc:UIScrollView+IBHelper.m

#import "UIScrollView+IBHelper.h"

@implementation UIScrollView (IBHelper)

/**
* Don't Use Auto Layout at IB, or this method doesn't work!!
*/
- (__unused void)setInitContentSize:(BOOL)optim {
	if (!optim) return;
	UIView *child = self.subviews.firstObject;
	self.contentSize = child.bounds.size;
}

@end
```

あとはストーリーボードで対象のUIScrollViewを選択してUser Defined Runtime AttributesにinitContentSizeをYESとでも設定してやってたんだ。

するといい感じにストーリーボードだけで設定できる。
（まだまだスマートとは言い難いがな〜。）

# でも悪夢はオートレイアウトが必要になってから始まった
iPhoneってiPhone5からの4インチとiHone4以前の3.5インチ画面がある。結構凝ったレイアウトのアプリを作ってるくせにストーリーボードを分けたくない！（分ける方法: http://goo.gl/jFkrdT）
これからiPhone6で新しいサイズの画面がまた増える噂やiPadの画面まで同じストーリーボードで作れるようになるよってWWDCでも言ってた。
これからの時代はiOSもアンドロイドのように様々なサイズの画面を一括で作る時代＝オートレイアウト必須。
でも上のやり方だとオートレイアウトを有効にすると相性が悪いんだ。
このサイトの説明を見てくれ。
http://goo.gl/IAPbHz

# 整理
上のリンクのサイトでは「viewDidLayoutSubviewsでsetContentSizeする」必要があると言っている。
じゃあviewDidLayoutSubviewsはUIViewControllerのメソッドなので大人しくUIViewControllerの継承クラスを作ってストーリーボード上のスクロールビューと紐付けてコード書くか？
嫌に決まってるだろー。
面倒ごとが嫌いじゃない奴はプログラマーじゃない。
合理的じゃない奴はプログラマじゃない。そうだろう？
よし、俺がたった数行の魔法の呪文を教えよう。
もちろん文句は聞く。後でな。(コメントにて爽やかにディスってください！)

# 準備
面倒は嫌いだ。だからCocoaPodsでいくつかライブラリを入れてもらうぜ。

下記の3つのライブラリを使うぜ。

```:Podfile
platform :ios, "7.1"
inhibit_all_warnings!

pod "BlockInjection", "~> 0.6.4"
pod "UIView+Helpers", "~> 1.4.0"
pod "BlocksKit", "~> 2.2.3"
```

万が一Cocoapods使わずにやってきた人居たら損してる。だから下のリンクを読んでくれ。
http://goo.gl/TiaLXi

じゃあ本題に入る。

# このコードでIt's Automatic!!ダイスケ的にもオールOK
じゃあ製作中のアプリのデリゲートを開いてくれ！
別にバヤイによってはデリゲートじゃなくてもいいけど、アプリが起動してすぐに一度だけ実行されたらいいコードなので書く場所は融通きかせてくれて構わない。
まあ無難なのはデリゲートの
-(BOOL)application:didFinishLaunchingWithOptions:
メソッドだじょー。

まずはこいつらをインポートしてくれ。

```objc:AppDelegate.m
#import <BlockInjection/BILib.h>
#import <BlocksKit/NSArray+BlocksKit.h>
#import <UIView+Helpers/UIView+Helpers.h>
#import <objc/runtime.h>
#import "AppDelegate.h"
```

で-(BOOL)application:didFinishLaunchingWithOptions:に書くとしたら

```objc:AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
	// fix contentSize of all UIScrollViews automatically
	[BILib injectToClassWithName:@"UIViewController" methodName:@"viewDidLayoutSubviews" postprocess:^(UIViewController *instance) {
		static void const *key = "fixed";
		// flag == YES -> return, or NO -> resume
		if ([objc_getAssociatedObject(instance, key) boolValue]) return;
		NSArray *scrollViews = [instance.view subviewsOfClass:[UIScrollView class] recursive:YES];
		[scrollViews bk_each:^(UIScrollView *scrollView) {
			UIView *container = scrollView.subviews.firstObject;
			scrollView.contentSize = container.bounds.size;
			DDLogVerbose(@"AppDelegate: contentSize of an UIScrollView is changed to %.0fx%.0f", scrollView.contentSize.width, scrollView.contentSize.height);
		}];
		// flag = YES
		objc_setAssociatedObject(instance, key, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
		[instance.childViewControllers bk_each:^(UIViewController *childViewController) {
			objc_setAssociatedObject(childViewController, key, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
		}];
	}];
	return YES;
}
```

コピペで貼ってもらっちゃったらいいと思う。
あとはな〜んにもしなくてもストーリーボードで設置したスクロールビューが全部いい感じにスクロールできるようになってる。
その都度なんのコードを書く必要もない。

もちろんこのコードの前後に通常通りその他のコードを書いて構わない。

# XIBファイルで生成したビューを追加するときにスクロールビューが含まれてたらどうすんのよ？
xibファイル（もしくはnibファイル）でビューを追加する場面というのは、おそらく画面の一部をストーリーボードとは別に作っておいて、それをストーリボードから来る画面の上にパーツとして乗せたりするんだと思う。
しかしそういう使い方の時はxibで作られるビューに対してはオートレイアウトが必要ないことが多い。（画面全体を覆うほど大きなビューにならない為、画面サイズに応じて変化することを求められない場合が多いという意味）
なのでxibファイルにスクロールビューが含まれるときはそのファイルだけオートレイアウトはオフにし最初のほうに書いたやり方（User Defined Runtime Attributesを使うやり方）でいいと思う。

# 〆
好きに使ってくれて問題ないですが先程のもっさりしたコードの解説は省略します。すんません。