フロントエンド特化型エンジニアが社内開発のため、主導してアニメーションに関するアプリを作った話(実装編)


実装編

本記事は、note 「フロントエンド特化型エンジニアが社内開発のため、主導してアニメーションに関するアプリを作った話」(https://note.mu/notes/n552acd33e64f) の、つくりの話になります。

どういうことを想って、どういうものを作って、どういう効果が得られたのか、等々、様々なこのアプリ開発にまつわる話はnoteの方を見ていただけると幸いです。

ざっくりとだけ言うと、自社で開発されていくアプリたちの心地よさという面のベースを保証するために、自社で開発した数々のアプリのアニメーションを見て参考にしたり話し合ったりできる、カタログ的なアプリを作った話です。
また、そのアプリを、より実務的に使うために、このアプリ上でアニメーションの変化値(ボタンタップ時の透明度や色)を設定できるようにし、エンジニア以外がアニメーションを実質実装できる状況を作った話です。

ここでは、そのアプリの要件である、自分以外もこのアプリにアニメーションを追加していく、を実現するため、できるだけ楽に、かつこのアプリの統一されたスタイルを崩さないように、各アプリからコードを移植することを考えて書いたコードたちを紹介する感じです。
中身的には非常にシンプルなもので、こういうことを意識したっていう話だと思って気楽に見てください。

画面ごとの実装方法

一覧画面

まずは一覧画面。
ここは、見る対象のアニメーションを選択するという目的の画面で、どういうタイプ(カテゴリ)のアニメーションかというsectionの中に、各アニメーションがrowとして存在する形になります。
そのため

  • 属するカテゴリを決める(無ければカテゴリを作成する)
  • アニメーションの名前を決め、所属カテゴリのアイテムとして追加する

CategoryDataというenumで、各カテゴリ名と、それに含まれる項目を管理


enum CategoryData {
    case view
    case label
    ...

    var title: String {
        switch self {
        case .view  : return "View"
        case .label : return "Label"
        ...
        }
    }

    var children: [TargetData] {
        switch self {
        case .view:
            return [
                .hoverView,
                .jumpingImageView,
            ]
        case .label:
            return [
                .animateValueLabel,
            ]
        ...
        }
    }
}

  
TargetDataというenumで、各アニメーション名と対象ViewControllerを管理
ViewControllerの名前は、ViewControllerFor{アニメーション名}という形式に統一することで、アプリ上で見たアニメーションがどれかわかりやすくなる。

enum TargetData {
    case hoverView
    case jumpingImageView
    case animateValueLabel
    ...

    var title: String {
        switch self {
        case .hoverView         : return "HoverView"
        case .jumpingImageView  : return "JumpingImageView"
        case .animateValueLabel : return "AnimateValueLabel"
        ...
        }
    }

    var target: UIViewController {
        var vc: UIViewController
        switch self {
        case .hoverView:
            vc = ViewControllerForHoverView.instantiate()
        case .jumpingImageView:
            vc = ViewControllerForJumpingImageView.instantiate()
        case .animateValueLabel:
            vc = ViewControllerForAnimateValueLabel.instantiate()
        ...
        }
        vc.title = self.title
        return vc
    }
}

詳細画面

んで、詳細画面。
こちらは基本的にはそのアニメーションの実装によるので、実装についてはほぼほぼおまかせって感じ。
ただ、少し決まりを作って、統一を図ります。

  • ラジオボタン / チェックボックスで動作させる場合

共通で使うラジオボタン(RadioView)・チェックボックス(CheckView)を作成しているので、それを使います。

例えば、こんな感じでCheckViewを作成し、stackViewに追加していく。

let checkViewType: [String] = [
    "alpha",
    "scale",
    "highlight",
    ]

self.checkViewList = checkViewType.enumerated().map { content -> CheckView in
    let checkView = CheckView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 98))
    checkView.delegate = self
    checkView.setData(id: content.offset, title: content.element)
    return checkView
}
self.horizontalStackView.setViews(self.checkViewList)

HorizontalStackViewおよびアニメーションパーツを画像のように配置する。

  • ボタンで動作させる場合

共通で使うボタン(TriggerButton)を作成しているので、それを使います。

TriggerButtonをサイズplaceholderで画像のように配置する。

このように、最低限の決まりをビジュアルとして残しておくだけでも、だいぶ変わります。少々面倒なことがもたらす効果は絶大だ、とはよくいったものです。

おわり

使用するコンポーネントやレイアウト等、実装に必要な情報を揃えておくと、追加する側に優しい感じになるかなって。
そんな思いで、設計をした話でした。