Swiftで便利なライブラリやUIパーツに工夫を凝らしてUIを彩るサンプル例（ライブラリ選定とベース作成）

## 1. はじめに

iOSアプリを開発するにあたって機能の実装と同じくらいに頭を悩ませる部分でもありながら、楽しい？部分でもあるのがUI設計だと思います。しかしながら、CocoaPodsでインストールできるライブラリの中でもUIやアニメーションに関わるものは多数あり、用途によっても組み合わせも色々です。

私自身はiOSアプリを開発する際には、実はできるだけライブラリに頼らないで作れないか？を考えるタイプではありますが、公開されているライブラリのGithubのREADMEをチェックしたり、実際の挙動を確認するために下記のサイト等をチェックしています。

+ [iOS Cookies](http://www.ioscookies.com)

UIに関するライブラリに限らずとも、ライブラリを使う際には最新のバージョンへSwiftへの対応がなされているかをチェックしておいて下さい。(今回は元々Objective-Cで作られていますが、Bridging-HeaderでSwiftに対応させているものも使用しています)

今回は数あるライブラリの中でアプリのUIを綺麗にするための実装例を紹介します。

※他にも下記のようなライブラリを集めたサイトもありますので、ライブラリを探す際やアニメーション等の動きの参考にしてみるのも良いかと思います。

+ [Cocoa Controls](https://www.cocoacontrols.com)
+ [awesome-ios-ui](https://github.com/cjwirth/awesome-ios-ui)

情報提供して頂きました[@takasek](https://twitter.com/takasek)さんありがとうございます！

2016.08.31追加：ライブラリを探す際にはこちらも参考になると思いますので、合わせて共有致します。

+ [COCOAPODS.ORG](https://cocoapods.org/)
+ [CocoaPodsSearch](http://umememo.com/archives/85)


## 2. 今回のサンプル概要と使ったライブラリの紹介

上記のようにライブラリも千差万別で何を選んで良いか正直迷うところでしたが、一応現在の最新バージョンのSwiftで問題なく機能するものを、要所要所で使用して一工夫する方針で進めました。
（Swift3.0にバージョンアップした暁には修正したり、ライブラリの部分を独自実装で代替するかは検討中です）

+ [(Github) CocoaPodsのライブラリとParallax効果を組み合わせたUIサンプル](https://github.com/fumiyasac/LittleRichUISample)

※サンプルに対してのご意見やご要望等はお気軽にお願いしますm(_ _)m

######☆2-1. 今回のサンプル概要

今回のサンプルを作成した環境は下記の通りです。
※ 現在はSwift3.0系のものがmasterブランチになっています。

+ Xcode7.3
+ Swift2.2
+ MacOS X El Capitan (Ver10.11.6)

※写真については[写真AC](http://www.photo-ac.com/)様のフリー素材を使用しています。


__①メイン画面時:__

![beauty_ui1.png](https://qiita-image-store.s3.amazonaws.com/0/17400/b70abd78-46c4-f629-237e-a19c5fb4bd12.png)

__②左端もしくは右端スワイプ時:__

![beauty_ui2.png](https://qiita-image-store.s3.amazonaws.com/0/17400/c3c3a74b-8dde-da06-3d86-c16d44ffaa4f.png)

__③「Tour」ボタンを押下して遷移時:__

![beauty_ui3.png](https://qiita-image-store.s3.amazonaws.com/0/17400/8ac4ab12-245b-a2cd-90b5-dabba0245c90.png)

メインの画面にライブラリで一工夫を加えて、少しおしゃれな感じで組み合わせてみた感じに仕上げました。

######☆2-2. 使用したライブラリ一覧

利用してみたライブラリの一覧は下記になります。

+ __[SlideMenuControllerSwift](https://github.com/dekatotoro/SlideMenuControllerSwift)__

アプリ画面の左端もしくは右端をドラッグするとニョキッと隠れていたサイドメニューを表示させる機能をつけてくれるライブラリです。ライブラリの導入の際は「[【Swift】スライドメニューライブラリSlideMenuControllerSwiftを使ってみた](http://nukenuke.hatenablog.com/entry/2015/07/16/181531)」の記事参考にしました。

__ライブラリ「SlideMenuControllerSwift」に関する記事：__
[【iOS】SwiftでiQONとかGoogle+ぽいスライドメニューライブラリつくったよ](http://qiita.com/dekatotoro/items/efb76c27b6a3f58cab48)

※今回のサンプルではまだメニューの中身は実装していないのでそこはまた後ほど実装予定です。

+ __[LTMorphingLabel](https://github.com/lexrus/LTMorphingLabel)__

UILabelに対してアニメーションを付与してくれる定番のライブラリになります。今回はラベル表示のアニメーションでちょこっと使用しています。ライブラリ導入の際は「[LTMorphingLabelでUILabelをモーフィング](http://llcc.hatenablog.com/entry/2016/06/12/223000)」の記事を参考にしました。

+ __[Gecco](https://github.com/yukiasai/Gecco)__

アプリ内でのチュートリアルや説明画面でのハイライト表記をするライブラリになります。こちらはGithub内にある導入サンプルを元に若干のアレンジを加えた実装をしています。

__ライブラリ「Gecco」に関する記事：__
[チュートリアルなどで使えるスポットライトっぽいUIを作ってみました](http://qiita.com/yukiasai/items/15666ce06f5436cf8e40)

+ __[BubbleTransition](https://github.com/andreamazz/BubbleTransition)__

READMEのスクリーンショットにもあるようにモーダルのトランジションをおしゃれにしてくれるライブラリになります。こちらはGithub内にある導入サンプルを元に実装をしています。

+ __[HMSegmentedControl](https://github.com/HeshamMegid/HMSegmentedControl)__

このライブラリはObjective-C製のライブラリになります。UISegmentedControlに装飾を加えて、見た目をカスタマイズするためのライブラリになります。こちらはGithub内にある導入サンプルを元に実装をしています。

このサンプルのPodfileは下記のようになります。

```
platform :ios, '8.0'
use_frameworks!
target 'LittleRichUISample' do
  pod 'SlideMenuControllerSwift'
  pod 'BubbleTransition'
  pod 'LTMorphingLabel'
  pod 'Gecco'
end
```

HMSegmentedControlに関しては、Github内にある`HMSegmentedControl.m`と`HMSegmentedControl.h`をプロジェクト内に直接導入してBridging-Headerファイルを経由して使用しています。

######☆2-3. 今回独自に実装を行ったParallax（視差効果）の部分について

このサンプルのUITableView部分及びサンプル内の「Tour」ボタンの遷移先の背景画像に関しては、Parallax（以下パララックスと表記）を実装してスクロール時の動きに一工夫をしています。

+ パララックスの説明参考：[パララックス入門～スクロールで魅力あるWebサイトに～
](https://www.wan55.co.jp/column/detail/id=313)

こちらはWebサイトやアプリ等でも実装されている例は多くありますし、デザインによってはとてもカッコよく見えます。UITableViewで実現する場合の実装例もライブラリを組み合わせた実装例と併せて紹介できればと思います。

__JFYI: UITableViewのパララックスを実装のする上で参考にしたサイト__

+ [ParallaxAutoLayoutDemo](https://github.com/runmad/ParallaxAutoLayoutDemo)
+ [ParallaxCells-iOS](https://github.com/krishan711/ParallaxCells-iOS)
+ [Gorgeous parallax scrolling with UITableViewCells](https://blog.krishan711.com/gorgeous-parallax-scrolling-with-uitableviewcells)
+ [[iOS][Swift] パララックス(視差効果)を入れるサンプル](http://dev.classmethod.jp/smartphone/iphone/ios_ui_parallax/)

ほとんどが英語のドキュメントやサンプル例にはなりますが、上記の記事やGithubへのリンクにはサンプルソースや解説がありますので試してみると楽しいかもしれません。

## 3. 実装及び設計のポイント

ライブラリの実装部分に関しては、なかなか言葉ではうまく伝わらないかもしれないところがあるかもしれませんが、ポイントとなりそうな部分については下記の5点になります。

######☆3-1. SlideMenuControllerSwiftの左右メニュー部分の実装ポイント

まずは左右のメニューになるViewControllerをStoryboard上にそれぞれ用意しておき、またそれぞれの`ViewController.swift`と関連付けます。そして、下記のIdentityの項目のStoryBoard IDを各ViewControllerに設定します。

![storyboard_identity.png](https://qiita-image-store.s3.amazonaws.com/0/17400/f0e29eb3-4b5f-12be-a447-54236bee1200.png)

これでStoryBoardの準備が整ったので、次に`AppDelegate.swift`に対して下記のように追記をおこないます。

```swift:AppDelegate.swift
import UIKit
import SlideMenuControllerSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        //使用しているStoryBoardを取得
        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        //左メニュー用のViewControllerを取得
        let leftViewController = storyboard.instantiateViewControllerWithIdentifier("LeftMenuViewController") as! LeftMenuViewController

        //右メニュー用のViewControllerを取得
        let rightViewController = storyboard.instantiateViewControllerWithIdentifier("RightMenuViewController") as! RightMenuViewController

        //メイン用のViewControllerを取得
        let viewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController

        //SlideMenuControllerの設定を行う
        let slideMenuController = SlideMenuController(mainViewController: viewController, leftMenuViewController: leftViewController, rightMenuViewController: rightViewController)
        self.window?.rootViewController = slideMenuController
        self.window?.makeKeyAndVisible()
        
        // Override point for customization after application launch.
        return true
    }

・・・（省略）・・・
```

手順としては、ライブラリのインポート宣言を行った後に、`didFinishLaunchingWithOptions`メソッド内に左右メニューになるViewControllerと一番最初に呼び出されるViewControllerを設定し`SlideMenuController()`内に設定してOKです。左端もしくは右端をスワイプすると左右から設定したViewControllerが表れるようになります。

######☆3-2. Gecco+LTMorphingLabelでのサブコンテンツ部分の実装ポイント

Geccoを利用して少し変則的なポップアップメニューライクなUIを実現します。また右上の押したボタンの中央部分はここを押したという意味も込めて円でマスキングしています。

+ `ViewController.swift`の画面に`DescriptionViewController.swift(NewinfoViewController.swift)`を重ねる

という形になります。

まずはメイン画面の`ViewController.swift`に下記のように設定をします。

```swift:ViewController.swift

//ライブラリ「Gecco」を介して表示されるコントローラーを表示するアクション
@IBAction func descriptionAlertAction(sender: AnyObject) {
    doDescriptionAlert()
}

@IBAction func newinfoAlertAction(sender: AnyObject) {
    doNewinfoAlert()
}

//ライブラリ「Gecco」を介して表示されるコントローラーを決める
private func doDescriptionAlert() {

    let descriptionViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DescriptionAlert") as! DescriptionViewController
    descriptionViewController.alpha = 0.75
    presentViewController(descriptionViewController, animated: true, completion: nil)
}

private func doNewinfoAlert() {

    let newinfoViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("NewinfoAlert") as! NewinfoViewController
    newinfoViewController.alpha = 0.75
    presentViewController(newinfoViewController, animated: true, completion: nil)
}
```

次に新しく`DescriptionViewController.swift(NewinfoViewController.swift)`と対応するViewControllerを作成し、下記のようにします。

```swift:DescriptionViewController.swift
import UIKit
import Gecco
import LTMorphingLabel

class DescriptionViewController: SpotlightViewController, LTMorphingLabelDelegate {

    private var stepIndex: Int = 0

    //タイトル部分のラベル（Storyboard上では初期値を空にしておく）
    @IBOutlet weak var titleLabel: LTMorphingLabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //ラベルの値とLTMorphingLabelのモーションを設定する
        titleLabel.text = "Description"
        titleLabel.morphingEffect = .Evaporate
        
        //ライブラリ「Gecco」のSpotlightViewControllerのデリゲートを設定
        delegate = self
    }

    //ライブラリ「Gecco」で表示されるコントローラーがタップ回数をカウントして値に応じてアクションを変える
    func next(labelAnimated: Bool) {

        //コントローラーのアルファ値を変更
        updateAnnotationView(labelAnimated)
        
        let screenSize = UIScreen.mainScreen().bounds.size
        switch stepIndex {
            case 0:
                //このコントローラーを表示させて押したボタンのところにマスク表示をする
                spotlightView.appear(Spotlight.Oval(center: CGPointMake(screenSize.width - 38, 42), diameter: 36))
            case 1:
                //このコントローラーを閉じる
                dismissViewControllerAnimated(true, completion: nil)
            default:
                break
        }

        //タップ回数を1加算
        stepIndex += 1
    }

    //Geccoで表示されるコントローラーのアルファ値を変更する
    func updateAnnotationView(animated: Bool) {
        UIView.animateWithDuration(animated ? 0.55 : 0) {
            self.view.alpha = self.stepIndex == self.stepIndex ? 1 : 0
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

extension DescriptionViewController: SpotlightViewControllerDelegate {

    //Geccoで表示されている途中の場合
    func spotlightViewControllerWillPresent(viewController: SpotlightViewController, animated: Bool) {
        next(false)
    }

    //Geccoで表示されるコントローラーをタップした場合
    func spotlightViewControllerTapped(viewController: SpotlightViewController, isInsideSpotlight: Bool) {
        next(true)
    }

    //Geccoで表示されるコントローラーが閉じられる場合
    func spotlightViewControllerWillDismiss(viewController: SpotlightViewController, animated: Bool) {
        spotlightView.disappear()
    }
}
```

この部分の動きとしては`ViewController.swift`の表示するアクションを押下すると押したボタンの中央部分がマスキングされて、かつ遷移先の画面が表示されます。また、表示の際にタイトル部分のラベルにもLTMorphingLabelを使ってふわっと表示させるようにしています。

LTMorphingLabelのインポート宣言を行った後に、StoryBoardにラベルを配置してテキストを空の状態にしておきます。そして配置したラベルを選択状態にして下記のようにCustom Classの部分をLTMorphingLabelにて、IBOutletをつなげれば、`@IBOutlet weak var titleLabel: LTMorphingLabel!`となります。

![custom_class_settings.png](https://qiita-image-store.s3.amazonaws.com/0/17400/c9bbd515-7b85-cfeb-32d5-a5a1260b968a.png)

これであとは表示時の文言とLTMorphingLabelの動きを設定すれば、ちょっとわかりずらいかもしれませんがGeccoでの画面表示時にタイトルがしたからふわっと出てくる動きが実現します。

######☆3-3. BubbleTransitionを利用したセグエ部分の実装ポイント

ボタンの中心から円が広がっていくように遷移をするアクションを作っていきます。
まずは遷移元のViewControllerから遷移先のViewControllerに従来通りSegueを貼ります。（kindは「Present Modally」に設定）

![bubble_segue.png](https://qiita-image-store.s3.amazonaws.com/0/17400/f1cfc1d3-b983-c037-63c2-f73ae5161637.png)

そして遷移元のViewControllerにBubbleTransitionの設定を行っていきます。
追記する部分は下記のようになります。

```swift:ViewController.swift
import UIKit
import BubbleTransition

class ViewController: UIViewController {
    
    ・・・（省略）・・・

    @IBOutlet weak var goSecondButton: UIButton!    

    //ライブラリ「BubbleTransition」のインスタンスを作る
    private let transition = BubbleTransition()

    ・・・（省略）・・・

    //BubbleTransitionを利用したセグエの設定
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        let controller = segue.destinationViewController
        controller.transitioningDelegate = self
        controller.modalPresentationStyle = .Custom
    }

    ・・・（省略）・・・
}

/**
 * BubbleTransitionのための設定をUIViewControllerTransitioningDelegateに記述
 * JFYI: https://github.com/andreamazz/BubbleTransition
 */
extension ViewController: UIViewControllerTransitioningDelegate {

    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        transition.transitionMode = .Present
        transition.startingPoint = goSecondButton.center
        transition.bubbleColor = goSecondButton.backgroundColor!
        return transition
    }
    
    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        transition.transitionMode = .Dismiss
        transition.startingPoint = goSecondButton.center
        transition.bubbleColor = goSecondButton.backgroundColor!
        return transition
    }

}
```

戻る際にはほとんど従来通りの実装で問題ないので、サンプル内の`SecondViewController.swift`の中やStoryboardの設定や配色を参考にして頂ければ幸いです。

######☆3-4. HMSegmentedControlでのメッセージ切り替え部分の実装ポイント

こちらに関してはStoryBoardを使用せずにコードを利用してプログラムで配置する形で実装しています。今回は文字の下にアンダーラインが表示されており、アンダーラインがない文字を押すとアンダーラインが動くようなUIにしてみようと思います。

基本的には、HMSegmentedControlにはかなり細かいプロパティが用意されているのでそれを活用して見た目を変えてみました。

```swift:SecondViewController.swift
//HMSegmentedControlを利用するSegmentedControl
var tabberSegmentedControl: HMSegmentedControl!

・・・（省略）・・・

override func viewDidLoad() {
    super.viewDidLoad()

    ・・・（省略）・・・
    
    //Objective-CのライブラリであるHMSegmentedControlの設定と配置

    //HMSegmentedControl要素作成
    tabberSegmentedControl = HMSegmentedControl(sectionTitles: ["First Messages", "Second Messages"])

    //押された際の設定
    tabberSegmentedControl.addTarget(self, action: #selector(SecondViewController.segmentControlTapped), forControlEvents: .ValueChanged)

    //HMSegmentedControl要素のサイズと位置設定
    tabberSegmentedControl.frame = CGRectMake(0, 60, view.frame.width, 50)

    //HMSegmentedControl要素の背景色設定
    tabberSegmentedControl.backgroundColor = UIColor.clearColor()

    //HMSegmentedControl要素の文字色・文字サイズ・フォント設定
    tabberSegmentedControl.titleTextAttributes = [
        NSForegroundColorAttributeName: UIColor.whiteColor(),
        NSFontAttributeName: UIFont(name: "Optima", size: 14)!
    ]

    //スライドするアンダーバーの高さ・色・位置設定
    tabberSegmentedControl.selectionIndicatorHeight = 2.0
    tabberSegmentedControl.selectionIndicatorColor = UIColor.whiteColor()
    tabberSegmentedControl.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationDown
    self.view.addSubview(tabberSegmentedControl)
    
    ・・・（省略）・・・
}
```

なかなかデザインカスタマイズの余地のなさげな、UISegmentedControlではありますが、ライブラリをうまく活用することでよりデザイン部分にもこだわることができそうです。
（どんなプロパティやメソッドがあるのかということは中のObjective-Cファイルを見ないと詳細がわからないのでそこはしょうがないかもですね。）

######☆3-5. UITableViewのパララックス部分の実装ポイント

この部分に関してはライブラリとは直接関係がない部分ですが、取り組んでみて面白い＆UI的に興味深い部分でもあるのでポイントをかいつまんだ上で解説します。

ポイントとしては、

1. UITableViewCellの画像の上下の制約の値を動的に変更するメソッドを作成する
2. UITableViewのScrollViewDelegateを利用してスクロールの変化量に応じて1.で作成したメソッドを呼び出す

の2点となります。

まずはUITableViewCellをInterfaceBuilderで作成する際に（今回はXibファイルを使っています）背景となるUIImageViewに上下左右0の制約をつけた後に上と下の制約に関しては値を変更できるように、図のようにXibに対応するクラスへOutlet接続をしておきます。

またセルの背景になるUIImageViewの画像アスペクトの設定は「Aspect Fill」としておきます。
※UIImageViewに入れる画像に関しては、パララックス用に縦サイズを少し広めにとっておくと良いかと思います。

![cell_constraints.png](https://qiita-image-store.s3.amazonaws.com/0/17400/1758ecb7-ecee-7bf6-36e9-52c46f8f52e0.png)

そしてこのXibに対応するクラスに制約を動的に変更するメソッドを下記のように記述します。

```swift:ParallaxTableViewCell.swift
import UIKit

class ParallaxTableViewCell: UITableViewCell {

    //セルに配置されたUI部品
    @IBOutlet weak var backgroundCellImage: UIImageView!
    @IBOutlet weak var titleCellLabel: UILabel!
    @IBOutlet weak var descriptionCellLabel: UILabel!

    //画像の上の制約（backgroundCellImageの上の制約を「Ctrl+ドラッグ」でOutlet接続をする）
    @IBOutlet weak var imgTopConstraint: NSLayoutConstraint!

    //画像の下の制約（backgroundCellImageの下の制約を「Ctrl+ドラッグ」でOutlet接続をする）
    @IBOutlet weak var imgBottomConstraint: NSLayoutConstraint!

    //視差効果のズレを生むための定数（大きいほど視差効果が大きい）
    let imageParallaxFactor: CGFloat = 100

    //視差効果の計算用の変数
    var imgBackTopInitial: CGFloat!
    var imgBackBottomInitial: CGFloat!

    //CellModelに値がセットされたら各部品にその値を格納する
    var model: CellModel! {
        didSet {
            updateView()
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        //意図的にずらした値を視差効果の計算用の変数にそれぞれ格納する
        clipsToBounds = true
        imgBottomConstraint.constant -= 2 * imageParallaxFactor
        imgBackTopInitial = imgTopConstraint.constant
        imgBackBottomInitial = imgBottomConstraint.constant
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

    //セルに配置した部品の中に値を入れる
    func updateView() {
        backgroundCellImage.image = UIImage(named: model.imageName)
        descriptionCellLabel.text = model.desc
        titleCellLabel.text = model.title
    }

    //backgroundCellImageにかけられているAutoLayoutの制約を再計算して制約をかけ直す
    func setBackgroundOffset(offset: CGFloat) {
        let boundOffset = max(0, min(1, offset))
        let pixelOffset = (1 - boundOffset) * 2 * imageParallaxFactor
        imgTopConstraint.constant = imgBackTopInitial - pixelOffset
        imgBottomConstraint.constant = imgBackBottomInitial + pixelOffset
    }

}
```

これでUITableViewのセル側の準備が整いましたので、次にUITableViewが配置されている部分の実装に入っていきます。

UITableViewにあるUIScrollViewDelegateを利用して、UITableViewをスクロールした際にUITableViewCell側で定義している`setBackgroundOffset(offset: CGFloat)`メソッドを呼び出して、スクロール量を元にセル内のUIImageViewの上下の制約の値を変化させてパララックス効果を実現します。

下記が該当部分の実装ロジックになります。

```swift:ViewController.swift
//UITableViewで発火するUIScrollViewDelegateを拡張する
extension ViewController: UIScrollViewDelegate {

    func scrollViewDidScroll(scrollView: UIScrollView) {

        //パララックスをするテーブルビューの場合は画面に表示されているセルの画像のオフセット値を変更する
        if scrollView == parallaxTableView {
            for indexPath in parallaxTableView.indexPathsForVisibleRows! {
                setCellImageOffset(parallaxTableView.cellForRowAtIndexPath(indexPath) as! ParallaxTableViewCell, indexPath: indexPath)
            }
        }
    }

    //まだ表示されていないセルに対しても同様の効果をつける
    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        
        let imageCell = cell as! ParallaxTableViewCell
        setCellImageOffset(imageCell, indexPath: indexPath)
    }

    //UITableViewCell内のオフセット値を再計算して視差効果をつける
    private func setCellImageOffset(cell: ParallaxTableViewCell, indexPath: NSIndexPath) {
        
        let cellFrame = parallaxTableView.rectForRowAtIndexPath(indexPath)
        let cellFrameInTable = parallaxTableView.convertRect(cellFrame, toView: parallaxTableView.superview)
        let cellOffset = cellFrameInTable.origin.y + cellFrameInTable.size.height
        let tableHeight = parallaxTableView.bounds.size.height + cellFrameInTable.size.height
        let cellOffsetFactor = cellOffset / tableHeight
        
        cell.setBackgroundOffset(cellOffsetFactor)
    }
}
```

一見するととても面倒くさそうな感じがするかもしれませんが、上記の２ポイントを踏まえて処理の概要を知ることができれば、デザインと合わせて目を引くような表現が実現できるのではないかと思います。

※`SecondViewController.swift`の画面で使用しているパララックスの実装は「[[iOS][Swift] パララックス(視差効果)を入れるサンプル](http://dev.classmethod.jp/smartphone/iphone/ios_ui_parallax/)」を利用していますのでこの記事を参考にしてみてください。

## 4. 取り組んでみての雑感

今回のサンプル作成にあたり、特に注意を払った点としては、Storyboardとなるだけ相性が良さそうでかつモーションやアニメーションに関してもあまりガリガリ動くという感じではなく、ちょうど良くアクセントになる感じのものをチョイスしました。

ライブラリによっては最新版をサポートしていなかったり実装例に関する情報がなかなか見つからないものもあって使うものの選定は結構大変でした。

## 5. 最後に

iOSアプリの開発においてライブラリの活用は本当に便利だと感じる点は非常に感じますし、有名なライブラリはXCodeや言語のバージョンアップにもキャッチアップしていることが多いです。

非常に強力な反面、複数のライブラリを組み合わせによって思わぬところで弊害が出たり、ライブラリの仕様から逸脱してしまう改修や修正を伴う際に想像以上に工数を費やす結果になってしまう可能性もあるので、安易な多用をするのではなく、「ライブラリを使う or ライブラリに近しい挙動を自分で再現する」という選択を適切に選択するとよいのではないかと思います。

※ この記事を書いている私も初めて半年くらいまではCocoaPodsを使わずにiOSアプリを開発しました。確かにUIで凝ったことをしようとすると難しい時も多々ありましたが、UIKitの理解をするのには結構悪くないプラクティスかなとは思います。

今後はこのサンプルアプリに様々なロジックを加えていく予定ですので宜しくお願いします。

## 補足その1. XCode8 ＆ Swift2.3でこのサンプルを動かす際の作業手順

こちらのサンプルで導入しているライブラリに関しては、ほとんどはSwift2.3(Swift3.0)でも動くライブラリを使用しています。今回はCocoaPodsのバージョンも更新したので、備忘録としてまとめました。

XCodeを開き、まずはライブラリ以外で自分で実装した`xxx.swift`ファイルをXCodeの上ナビゲーションにあるメニューの`「Edit」 > 「Convert」 > 「To Current Swift Syntax」`を選択し、Swift2.3へコンバートをしてあげればOKです。

また、下記のキャプチャのように`Use Legacy Swift Language Version`をYESに設定もしておきます。

![use_legacy_version.png](https://qiita-image-store.s3.amazonaws.com/0/17400/37186849-b23e-b3fe-9555-2d92343db609.png)

（ライブラリに関するエラーはたくさん出ますが、ライブラリは後ほど入れ直すので今回は一旦無視してプロジェクトを閉じます）

次にCocoaPodsのバージョンを上げておきます。今回のアップデートのバージョンは`1.1.0.rc.2`になります。

```
$ sudo gem update cocoapods --pre
$ pods --version
```

このバージョンのCocoaPodsで配布されているライブラリを使用する際の記述は下記のようになります。
CocoaPodsで公式に配布されているものではなく、いくつかのライブラリは直接githubのブランチのものを使用するようにしています。

```Podfile
platform :ios, '8.0'
swift_version = '2.3'
use_frameworks!
target 'LittleRichUISample' do
  pod 'SlideMenuControllerSwift', git: 'https://github.com/dekatotoro/SlideMenuControllerSwift.git', branch: 'swift2.3'
  pod 'BubbleTransition', git: 'https://github.com/andreamazz/BubbleTransition', branch: 'swift2.3'
  pod 'LTMorphingLabel', git: 'https://github.com/lexrus/LTMorphingLabel.git', branch: 'master'
  pod 'Gecco'

  post_install do |installer|
    installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['SWIFT_VERSION'] = '2.3'
      end
    end
  end

end
```

Podfileの記述の修正が終わったら、一旦記載している`pod '(ライブラリ名)' ...`の部分をコメントアウトをして`$ pod install`を実行して、前に使用していたライブラリを綺麗にした後にコメントアウトを復活させて再度`$ pod install`を実行してSwift2.3対応のライブラリを導入してください。

また、今回のサンプルで使用しているPodfileの記述方法やXCodeの設定等に関しては下記の記事を参考にしました。

+ [既存のプロジェクトでSwift2.3のままXcode8でビルドを通す](http://yanamura.hatenablog.com/entry/2016/08/30/191018)
+ [Xcode8だけどSwift2.3で実行したい人向け対応](http://qiita.com/Hakota/items/4a5e9201c7dc17e9017a)
+ [Swift2.3/Swift3でCocoaPodsを使い始めようとしてハマったことメモ](http://qiita.com/yokomotod/items/a96ab24f01a91d54d399)

再度、XCodeプロジェクトを立ち上げて、再度ビルドを実行すればOKです、

XCode8.0 ＆ Swift2.3に対応したブランチは下記になりますので、ご確認をして頂ければ嬉しい限りです。

+ __[Swift2.3対応版のサンプル（LittleRichUISample）はこちら](https://github.com/fumiyasac/LittleRichUISample/tree/feature/swift2.3)__


## 補足その2. XCode8 ＆ Swift3.0対応に関して

基本的には、上記のSwift2.3対応とほとんど同様な手順`(Converterで「Swift3.0」を選択する)`となります（`Use Legacy Swift Language Version`の対応はこの時は必要ありません）。PodfileでインストールするライブラリをSwift3.0に対応したブランチを読み込むようにするために下記のようになります。

```Podfile
platform :ios, '8.0'
swift_version = '3.0'
use_frameworks!
target 'LittleRichUISample' do
  pod 'SlideMenuControllerSwift', git: 'https://github.com/dekatotoro/SlideMenuControllerSwift.git', branch: 'master'
  pod 'BubbleTransition', git: 'https://github.com/andreamazz/BubbleTransition', branch: 'master'
  pod 'LTMorphingLabel', git: 'https://github.com/lexrus/LTMorphingLabel', branch: 'swift3'
  pod 'Gecco', git: 'https://github.com/yukiasai/Gecco', branch: 'master'

  post_install do |installer|
    installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['SWIFT_VERSION'] = '3.0'
      end
    end
  end
end
```

※ Swiftのバージョンが上がった際には自分のプロジェクトで使用しているライブラリのブランチにあるコミットログやタグなどをこまめにチェックしておくと、ライブラリのキャッチアップがしやすくなるかと思います。

ライブラリの入れ替えとSwift3.0のConvert対応ができたら、プロジェクトを開いて、`SecondViewController.swift`の下記の部分を修正します（この1箇所以外はConverterの変換でうまくいきました）。

この部分は具体的には、UITextViewに表示するテキストにHTMLタグを適用するようにする設定の文字コードに関する部分になります。

+ 修正前 → `NSCharacterEncodingDocumentAttribute: String.Encoding.utf8 as AnyObject`
+ 修正後 → `NSCharacterEncodingDocumentAttribute: NSNumber(value: String.Encoding.utf8.rawValue) as AnyObject`

と変更をしてあげればOKです。
またテキストフィールドのHTML化設定部分の全体の処理は下記のようになります。

```swift:SecondViewController.swift
//テキストフィールドのHTML化設定を行う
let paragraph = NSMutableParagraphStyle()
paragraph.lineHeightMultiple = 1.5

let encodedData = htmlText.data(using: String.Encoding.utf8)!
let attributedOptions : [String : AnyObject] = [
    NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType as AnyObject,
    NSCharacterEncodingDocumentAttribute: NSNumber(value: String.Encoding.utf8.rawValue) as AnyObject,
    NSParagraphStyleAttributeName : paragraph
]

messageText.attributedText = attributedString
```

またこの対応の際に参考にした記事は下記になります。

+ [Swift3でUITextViewのAttributeの設定にて文字コード指定の部分でエラーが出てしまう](http://stackoverflow.com/questions/39643394/swift-3-error-swiftvalue-unsignedintegervalue-unrecognized-selector)

XCode8.0 ＆ Swift3.0に対応したブランチは下記になりますので、ご確認をして頂ければ嬉しい限りです。（現在はmasterブランチがSwift3.0対応したものになっています）

+ __[Swift3.0対応版のサンプル（LittleRichUISample）はこちら](https://github.com/fumiyasac/LittleRichUISample/)__

## 追記

2016.10.09:

+ Swift2.3対応のブランチの修正とSwift3.0対応のブランチを作成しました！

Swift3.0に対応したサンプルを使用する際には、下記の手順でブランチを変更して頂ければと思います。

```
$ git clone git@github.com:fumiyasac/LittleRichUISample.git
$ git checkout feature/swift3.0
```

2016.09.19:

+ Swift2.3対応のブランチを作成しました！

[XCode8＆Swift2.3で動作するソースはこちら](https://github.com/fumiyasac/LittleRichUISample/tree/feature/swift2.3)

下記の手順でブランチを変更して頂ければと思います。

```
$ git clone git@github.com:fumiyasac/LittleRichUISample.git
$ git checkout feature/swift2.3
```

2016.08.31:

+ 8/29に開催された「AKIBA.swift×Swift愛好会」にてこの記事の内容を発表してきました！

[クラスメソッド様のレポート記事はこちら](http://dev.classmethod.jp/smartphone/iphone/swift-lovexakiba-swift/)