Upload
fumiya-sakai
View
149
Download
0
Embed Size (px)
Citation preview
指の動きや遷移時等のアニメーションを
生かしたUIのサンプル解説
Swiftビギナーズ勉強会#21
2017/02/18FumiyaSakai
自己紹介と簡単な経歴など
✦ 今までの仕事履歴(本業)
石川県金沢市生まれ
本業はサーバーサイドのプログラマ※Rails&PHP使い
26歳〜32歳: Webプログラマ(PHP&Rubyがキャリア長い)
23歳〜25歳: Webデザイナー兼ディレクター
2017年3月よりiOSアプリ開発のエンジニアになります。
趣味:シルバーアクセサリー集め・スイーツ作り・アプリ開発
女子向け・グルメ・エンタメ関連のお仕事が多い
Qiita:http://qiita.com/fumiyasac@github
Github:https://github.com/fumiyasac
✦ 酒井文也(さかいふみや)
東京(大塚)住まいの32歳
こんな格好を普段からしているので遊び人に見られますがエンジニアです。
文系卒に思われますが実は数学科で理系卒です。
めっちゃお酒好きそうに見えますがビール苦手でお酒も超弱いです。
今でもたまにUIまわりとか触りたくなることがあったりなかったり
今年の4月からフリーランスです。(割とお堅い感じの会社にいます)
最近のはまっている食べ物はカボチャと担々麺と甘栗です。
最近はSwift以外ではRailsやLaravel・CakePHP・Node.jsなんかも
これまでに作ったもの(ネイティブアプリ)
①簡易家計簿アプリ「Coffre」
②ゲームアプリ「10秒虫食い算」
・カレンダーを自作しています
・シンプルなお小遣い帳感覚で支出管理できます
・全問正解者ほとんどいません…
・不定期ですがコラムも書いています
・サーバーサイドはRubyonRailsを使用
http://www.coffre.me/
・デザインにもこだわってみました(特にグラフ)
・実はちょっとバグがあります。
・問題は今後追加予定(現在110問収録)
個人的にはなりますが、他にもアプリ・Webサービスなど開発中です(2016年も宜しくお願いします)
・サイト等は次回のアップデートで公開予定
http://blog.just1factory.net/services/284
・若干の中毒性を含みます
カレンダーが好きでライブラリを作りました
日本の祝祭日を計算で出してくれる
・カレンダーアプリ等での活用を想定
・シルバーウィーク・ゴールデンウィークも対応
・ハッピーマンデー法の施行も対応
・春分の日・秋分の日にも対応
・過去の祝祭日もおおむね考慮はしている
構想や基本実装は僕ですが、他に4名のContributorのお力添えがあり実運用できるレベルになりました!
職人の手作業で計算しております!
・HTTP(HTTPS)通信は不要
★CalculateCalendarLogicver0.1.3
【最新情報】最近はmacOSのサポートがされました!※CocoaPods/Carthage経由で導入できます。
・Github:https://github.com/fumiyasac/handMadeCalendarAdvance
・実装解説:http://qiita.com/fumiyasac@github/items/33bfc07ad36dfffcdf8f
【2017年】Swift3系への正式対応をしていますので是非README等を参考に導入してみてください!
✦ 今回はできるだけ1つのアプリに近しい形になるようにしました
今回紹介するUIサンプルの概略図
食べたいもしくは作ってみたいレシピをアーカイブするアプリのUIに指の動きや遷移を工夫してみました。
✦ UI部分はライブラリなしでAPI通信時等の場面でライブラリを活用
使用したライブラリやサンプルについて
今回はコードを深掘りするよりも実装を行う前段階の設計やUIに絡む部分の実装ポイントをピックアップ。
★使用ライブラリの紹介
★今回の紹介する解説記事やGithubのサンプルについて
(Qiita)
ジェスチャーやカスタムトランジションを利用して入力時やコンテンツ表示時に一工夫を加えたUIの
実装ポイントまとめ
http://qiita.com/fumiyasac@github/items/6c4c2b909a821932be04
(Github)DraggableImageForm
https://github.com/fumiyasac/DraggableImageForm
ライブラリ名 ライブラリの機能概要
RealmSwift アプリ内のデータベース
SwiftyJSON JSONデータの解析をしやすくする
Alamofire HTTPないしはHTTPSのネットワーク通信用
KingFisher 画像URLからの非同期での画像表示とキャッシュサポート
CalculateCalendarLogic 日本の祝祭日の判定
✦ 画面や機能の役割に応じてStoryboardファイルの分割をしました
このサンプルの画面遷移とStoryboard
Storyboardファイルを分割することでチーム開発のconflict防止や機能毎の画面見通しを良くする効果も。
MakeRecipeController.swift ArchiveRecipeController.swift
AddController.swift GalleryController.swift GalleryDetailController.swift
Main.storyboard
(レシピアーカイブ作成) (レシピアーカイブ一覧)
(選択レシピ一覧) (選択レシピ詳細)
画面遷移時 CustomTransition.swift
(レシピアーカイブ追加)
Add.storyboard Gallery.storyboard
ViewController.swift
サイドメニュー&メインコンテンツ
(2つのContainerView)
Navigation Controller
✦ Alamofireの通信を司る部分を間に基底となる構造体をはさむ
APIとの通信を行う部分の実装
APIへのアクセス処理部分をAPIManager.swiftで管理をすることでControllerではパラメータを渡すだけに。
★Alamofireでの通信部分(APIからのデータ取得時)は下記のような構成
APIManager.swift
楽天レシピAPIからのデータ取得
(Alamofireを利用したデータ取得)MakeRecipeController.swift
Struct
実際の処理に関して:loadApiData(categoryId: String) { … } メソッドに処理の実体が記載されている。 ※ SwifyJSONでのJSONパースとUICollectionView表示用データ整形 ※ UICollectionViewのUserInteractionの制御も含む
CategoryList.swift(パラメータ用の大カテゴリ定義)
設定パラメータ一覧:
format:String
applicationId:String
categoryId:String
(レスポンス形式「json」)
(楽天WebサービスのAPIキー)
(大カテゴリのID)
Struct
使用している外部API:
楽天レシピカテゴリ別ランキングAPI
大カテゴリに該当するレシピデータを4件取得する
※このサンプルではデータ取得は「Add」ボタンがトリガー
✦ 1つのアーカイブデータに対してレシピのデータが多数紐づく形
レシピデータの保存方法とテーブル定義
プライマリキーの設定ができるのでデータ構造の見通しが良い(CoreDataはこの関連付けは暗黙的に行う)
★今回はRealmを使用してデータの保存・表示・削除を行っています
Many
Recipe.swift
Archive.swift
カラム名一覧:
id:Int
archive_id:Int
rakuten_id:String
rakuten_indication:String
rakuten_published:String
rakuten_image:String
rakuten_url:String
(このテーブルの一意ID)
(Archive.swiftで定義された一意ID)
(楽天レシピのID)
(楽天レシピの調理時間)
(楽天レシピの公開日)
(楽天レシピの画像URL)
(楽天レシピの詳細ページURL)
カラム名一覧:
id:Int
memo:String
created:Date
(このテーブルの一意ID)
(レシピアーカイブにつけるメモ)
(選択した日付)
One ポイントになる部分(Realmの特性を利用)
どのようなデータを持つかを決定する際に、 プライマリキー(一意になるID)を持つことができる ので、データの関連付けがしやすいと感じた。
※イメージ図
Archive (id=1)
Recipe (id=1)
(archive_id=1)
Recipe (id=n)
(archive_id=1)・・・
【ポイント】
Archive.id =
Recipe.archive_id
✦ UICollectionViewのセルにUILongPressGestureRecognizerを付与
レシピ画像をドラッグ&ドロップで選択
イベント発生までのタップ時間の設定や指のズレの許容範囲を設定できるのでより細かい設定が可能です。
★画像のドラッグ&ドロップに関する処理を行う直前のトリガーとして活用
//セルに表示する値を設定する internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { //セルの定義を行う let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecipeCell", for: indexPath) as! RecipeCell
//(省略)セルへ受け渡された値を設定する
//cellのタグを決定する(LongPressGestureRecognizerからの逆引き用に設定) cell.tag = indexPath.row //LongPressGestureRecognizerの定義を行う let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(MakeRecipeController.longPressCell(sender:))) //イベント発生までのタップ時間:0.24秒 longPressGesture.minimumPressDuration = 0.24 //指のズレを許容する範囲:10px longPressGesture.allowableMovement = 10.0 //セルに対してLongTapGestureRecognizerを付与する cell.addGestureRecognizer(longPressGesture) return cell }
longPressCellメソッド内で選択したセルの識別とドラッグ&ドロップの実処理部分を行う。
※TapGestureやPanGestureではダメなのか?
上記でも可能ではあるが、TapやPanではタイミングが早すぎるor動きとして不自然な感じがしたためにこの実装にしました。
✦ iOS7から遷移時のアニメーションを独自に定義することが可能
UIScrollView内に表示されているレシピ画像のサムネイルが画面全体に広がって表示されていくイメージ。
★本サンプルにおける設計は下記のような形を想定して実装する
CustomTransitionの設計
GalleryController.swift
カスタムトランジションの遷移イメージ
(選択レシピ一覧)
Tap1
UIScrollView
Tap2
presented : サムネイル画像が画面全体拡大
GalleryDetailController.swift
(選択レシピ詳細)
画面遷移時 CustomTransition.swift
詳細情報表示
Tap1
Tap2
どの方向の遷移かは、 var presenting: Bool
の状態で決定する
dismissed : サムネイル画像が元の位置に戻る
✦ NSObjectを継承&UIViewControllerAnimatedTransitioningを適用
アニメーションを行う実体となるContainerViewの中に遷移元・遷移先の情報とロジックを記載する形に。
★設定するのは動きの秒数と画面遷移時のアニメーション実体となるContainerView
CustomTransitionの実装(Classファイル)
//アニメーションの時間を定義する internal func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return (アニメーションの動作時間) } //アニメーションの実装を定義する internal func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
�//(流れ1) コンテキストを元にViewないしはViewControllerのインスタンスを取得する(存在しない場合は処理を終了)
�//(流れ2) アニメーションの実態となるコンテナビューを作成
�//(流れ3) 拡大・縮小等の処理を記載しアニメーションの実体となるContainerViewに必要なものを追加する ����UIView.animate(withDuration: (アニメーションの動作時間), delay: 0.0, options: .curveEaseOut, animations: { �����//アニメーション処理の中身 �}, completion:{ finished in ����transitionContext.completeTransition(true) �}) } transitionContext = 遷移元や遷移先のViewControllerやそのほか関連する情報が格納されているもの
✦ 遷移時のアクションのpresented側にtransitionDelegateを適用
遷移用クラスのインスタンスをViewController側で作りUIViewControllerTransitioningDelegateを適用する
★適用させるプロトコル:UIViewControllerTransitioningDelegate
CustomTransitionの基本例(Modal遷移)
//進む場合のアニメーションの設定を行う internal func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return (UIViewControllerAnimatedTansitioningのインスタンス) } //戻る場合のアニメーションの設定を行う internal func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return (UIViewControllerAnimatedTansitioningのインスタンス) }
このケースはModalでの遷移をCustomTransitionを活用してカスタマイズすることを想定しています。
遷移用のClass内でPresented/Dismissの場合分けが行えるような形で実装をしておく。
ポイント:
※UINavigationControllerと併用してPush/Popの遷移アニメーションのカスタマイズも可能
該当のViewControllerで遷移用Classのインスタンスを作成しPresented/Dismissの設定を行う。
✦ 基本はContainerViewを2枚使用したアニメーションを伴う実装
ContainerViewの設計及び実装の仕方によってはメニュー開閉用ライブラリのような動きを実装できます。
★メニュー部分の開閉用プロトコルを定義しておき処理実体は大元の部分で行う
コンテンツ下に隠れているメニューの実装
コンテンツ表示用とサブメニュー用のContainerViewの関係図
メニュー部分開閉処理に関して
Navigation Controller
MenuController.swift…
各メインコンテンツ (はじまりになる所)
MakeRecipeController.swift
Storyboard上での重なり順
UINavigationController (コンテンツ部分)…
ViewController.swift…
上
下
ViewController.swift … 各種プロトコルの実体処理を記載する
MenuController.swift … メニューを閉じるプロトコル(MenuCloseDelegate)を定義
MakeRecipeController.swift … メニューを開くプロトコル(MenuOpenDelegate)を定義
✦ 指の動きや遷移・アニメーションでUIに動きを加えた実装は面白い
今回のまとめ
ご清聴ありがとうございました!またこのような機会があった際には是非ともよろしくお願い致します!
★ユーザーの指の動きを想定したUIを組み立てる際の表現はとても面白い
選択時のドラッグ&ドロップの手前での要素検知やメニュー開閉のトリガーにも応用可能
★アニメーションを伴う実装もうまく組み合わせてさらにカッコいい表現も
ModalやNavigationControllerでの画面遷移にも工夫を加えることでより表現がリッチにできる
★デバッグや実装は慎重に行わないと思わぬところで落とし穴がある
UI部分の実装はテストコードだけでもバグや予期せぬ動作を検知しにくい部分なので注意
★自分ルール
【良いアウトプットのために】
発表・登壇時はこの中のいずれか2つを
絶対に準備するルールを設けています!