Slide 1

Slide 1 text

iOS/Android間でUI実装を近づけるヒントと道標 Fumiya Sakai potatotips #90 @ メルカリ様 2025/01/28

Slide 2

Slide 2 text

自己紹介 ・Fumiya Sakai ・Mobile Application Engineer アカウント: ・Twitter: https://twitter.com/fumiyasac ・Facebook: https://www.facebook.com/fumiya.sakai.37 ・Github: https://github.com/fumiyasac ・Qiita: https://qiita.com/fumiyasac@github 発表者: ・Born on September 21, 1984 これまでの歩み: Web Designer 2008 ~ 2010 Web Engineer 2012 ~ 2016 App Engineer 2017 ~ Now iOS / Android / sometimes Flutter

Slide 3

Slide 3 text

iOSのUI実装本を執筆しています! 書籍に掲載したサンプルのバージョンアップや続編等に現在着手中です。 少しの工夫で実現できるTIPS集やライブラリ表現の活用集をはじめとした、iOSア プリ開発の中でも特にUI実装やUIKitを利用した画面の中で特徴を与える様な表現 という題材に焦点を当てた書籍となっております。 現在は電子書籍版のみとなります。 こちらは全て¥1,000となっております。 https://just1factory.booth.pm/ 概要: https://book-tech.com/ 価格: 📖 Booth 📖 Book Tech

Slide 4

Slide 4 text

UI実装であると嬉しいレシピブックの最新情報 UI実装であると嬉しいレシピブックVol.3として2022年10月に商業化しました! Still WIP これまでの同人誌として頒布したものに加えて、Vol.1及びVol.2に頒布したものの 中で書籍に載せきれなかったものや表現や動きが特徴的でユーザーにもほんの少し 遊び心を与える様なUI実装を紹介したものをVol.3としています。 概要: これからの構想: こちらで購入可能です: Amazon / Google Play / Apple Books / KINOKUNIYA / Rakuten BOOKS etc.. 🏊 iOS: SwiftUIを利用したUI実装や動画関連の実装 🏊 Android: Jetpack Composeの基本やその他気になるUI表現の考察

Slide 5

Slide 5 text

技術書同人誌博覧会スタッフ&デザイン協力もしてます (御礼)第11回技術書同人誌博覧会参加ありがとうございました! 🍀 #9 一般参加募集チラシ 🍀 #10 次回案内チラシ 🍀 #11 次回案内チラシ 🍀 技書博 mini OSC案内

Slide 6

Slide 6 text

Androidではできるけど…iOSでは少し工夫が必要? Androidと同じ形でいけそうに見えるが実はそうではない?な事例紹介 意外と類似していそうで、実際はそうではない点がある事に注意 1. テキスト入力に関する事例: ユーザーの入力値の長さや改行に合わせて、入力フォームが伸縮する様な処理については、仕様次第では結構似ている形で作成 する事ができる場合もある。 2. Chipを使った表現事例: AndroidではCategory表示・TagCloud表示の様な文字サイズが異なるChip表示をする場合は純正のComponentが準備されている が、iOSではUICollectionView等を利用して工夫をする必要がある。 3. Scrollが関連する表現事例: AndroidのCoordinatorLayoutやNestedScrollViewを利用する事で、Scroll変化量を取得して位置によって表示を変化させる表現 は類似した様な表現をiOSではAnimation等を上手にりようしたカスタマイズが必要になる。

Slide 7

Slide 7 text

改行や長文テキスト入力に関する事例 Webでもよくある様な入力エリアが伸縮する様なフォームを考えてみます Ξϯέʔτͷճ౴ೖྗ ͍ͭ΋͋Γ͕ͱ͏͍͟͝·͢ɻ 初期状態では1行だけの表示になる。 ͪ͜Β͕๻͕ॻ͍ͨ࠷ॳͷετʔϦʔʹͳΓ ·͢ɻॳΊͯग़ձ͑ͨͷ͸͜ͷ৔ॴ͔ΒͰ͠ ͨɻͦͷ࣌͸·ͩ·ͩۦ͚ग़͠ͷ৽ਓͰͨ͠ 入力量が増えると下へ伸びるイメージ Ξϯέʔτͷճ౴ೖྗ Android OutlinedTextFieldを利用する事で実現可能。 Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter ) { var sampleText by remember { mutableStateOf(… テキストが入ります …) } OutlinedTextField( value = sampleText, onValueChange = { sampleText = it }, modifier = … , singleLine = false, maxLines = 7, … ) } https://developer.android.com/develop/ui/compose/text/user-input?hl=ja 参考コード例: ※この様に定めると、最大7行分まで伸縮する

Slide 8

Slide 8 text

(補足)iOSで自前で全てを作成する以外の選択肢 過去にはUItextViewとUIViewRepresentableを組み合わせて作成していました https://github.com/mshibanami/ResizingTextView ResizingTextView: iOS iOS16〜TextFieldが複数行に対応する様になった。 ※この様な形を作りやすくなった 仕様や事情によっては旧来通りのケースを利用する方 針で組み立てる必要があるかもしれない…

Slide 9

Slide 9 text

@Composable fun FliterChipEachRow( chipList: List, tempList: Set ) { var multipleChecked by rememberSavable { mutableStateOf(tempList) } FlowRow(… modifier/mainAxisSpacing/crossAxisSpacing等を設定する …) { chipList.forEachIndexed { index, s -> FilterChip( selected = … multipleChecked内で判定する …, onClick = { … multipleCheckedを増減させて見た目を切り替える … }, label = Text(text: s), border = … 状態によって見た目を変更する …, leadingIcon = … 状態によって見た目を変更する …, shape = RoundedCornerShape(8.dp) ) } } } Category表示・TagCloud表示の様なChip表示 文字の長さに合わせて上手く折り返す様なChip表示をする場合を考えてみます $IJQϑΟϧλʔαϯϓϧ Android FilterChipを利用する事で実現可能。 https://developer.android.com/develop/ui/compose/layouts/flow?hl=ja ※FilterChip & FlowRowを組み合わせて作る ✔︎ Chip1 Chip2 ✔︎ Chip3 Chip4 ✔︎ Chip5 https://developer.android.com/develop/ui/compose/components/chip?hl=ja FilterChipの他にも様々な形でのChip表示が 純正Componentとして準備されています。 デザインに合わせたある程度のカスタマイズ もする事ができます。

Slide 10

Slide 10 text

(補足)iOSで自前で全てを作成する以外の選択肢 UIKitを利用している際は、UICollectionViewをカスタマイズして作成する https://www.youtube.com/watch?v=dKe59TavIEc Tag CollectionView in Swift 5: iOS UICollectionViewでレイアウト属性をカスタマイズ ※レイアウト属性に関する実装が難しい 宣言的UIの登場によって従来よりも作成しやすくなっ たUI表現の1つかもしれない…

Slide 11

Slide 11 text

Scrollが関連する様な動きを実装する際のポイント CoordinatorLayoutを利用したHeader要素を折りたたむ様な表現を実装する 上方向へScrollするとTitle表記位置と背景イメージが隠れる表現: هࣄαϯϓϧλΠτϧ ˡ ˡ هࣄαϯϓϧλΠτϧ XML(Android View)を利用したLayout階層 - CoordinatorLayout - AppBarLayour - NestedScrollView - CollapsingToolbarLayout ・ImageView ・Toolbar - RecyclerView app:layout_scrollFlags =“scrll|exitUntilCollapsed” app:layout_collapseMode =“parallax” app:layout_collapseMode =“pin” ※iOSの場合はUIScrollViewDelegate & AutoLayoutの制約更新で実現する。 純正Component(UI要素)を組み合わせる事によって実現可能である

Slide 12

Slide 12 text

StickyHeaderの様な表現を実現する際の構造例 LazyColumn内のstickyHeaderを利用して構築する点が大きなポイント 上方向へScrollするとTab要素が残り続ける表現: λΠτϧ هࣄҰཡ χϡʔε هࣄҰཡ χϡʔε λΠτϧ @Composable fun ExampleScreen(…) { … 必要な処理を設定する … LazyColumn { item { … 上Scrollで隠れる対象要素を配置する … } sticyHeader { … 吸着するView要素を配置する … } when (tabSelected) { … 切り替え可能なScreen要素を配置する … } } } https://blog.studysapuri.jp/entry/2023/2/15/sticky-tab-header 参考: Jetpack ComposeでタブをSticky Headerにする方法 ※Tab要素は残り続ける

Slide 13

Slide 13 text

UIKitを利用した場合における類似した構成を作る場合 View要素の重なりにも配慮しなければいけないため結構難しい画面となる Storyboardによる概要と構成はこの様なイメージになります: λΠτϧ هࣄҰཡ χϡʔε هࣄҰཡ χϡʔε λΠτϧ ScrollView DetailHeaderView DetailSwitchButtonView UIStackView UITableView(1) UITableView(2)

Slide 14

Slide 14 text

類似した表現をSwiftUIで作成する際の構成 DroidKaigi2023で実装されていた事例を応用して作成したサンプルになります LazyVStackと配置したSection要素やUI構造を工夫する必要がある: λΠτϧ هࣄҰཡ χϡʔε هࣄҰཡ χϡʔε λΠτϧ https://github.com/fumiyasac/LikeCoodinatorLayoutExample SectionHeader要素のボタ ン背景に対するAnimation はGeometryReaderを利用し て実施する。 Offset値を監視してその値を 引き渡す。@Stateの変化を利 用して高さを変更する。 SwiftUIで作成する場合は、 LazyVStackを利用して作成す る点がポイントになる。

Slide 15

Slide 15 text

(補足)iOSで自前で全てを作成する以外の選択肢 ScalingHeaderScrollViewの利用でStickyHeaderの様な表現を作成可能 https://github.com/exyte/ScalingHeaderScrollView ScalingHeaderScrollView: iOS Header要素を利用した表現豊かな動きを作成する。 ※HeaderとContents要素を分けている GeomatryReaderからの値を応用するよりも、仕様や振 る舞いによっては効率的に実装が可能な場合もある。

Slide 16

Slide 16 text

Scroll変化量を取得して反映するNestedScrollを利用 NestedScrollを利用してScroll前後の運動量や速度を取得可能である Profile画面の配置要素がBar内にAnimationを伴って位置変更をする: ˡ XML(Jetpack Compose)を利用したLayout階層 - BoxConstraints - Box .nestedScroll(nestedScrollConnection) val nestedScrollConnection = remember { … scroll前の運動量保持 Profileのサムネイル画像と名前がHeaderのBar要素まで吸い込まれていく NestedScroll + Swipable + MotionLayoutの組み合わせ } onPreSecroll onPostSecroll onPostFling … scroll後の運動量保持 … scroll速度の反映

Slide 17

Slide 17 text

(補足)iOSで自前で全てを作成する以外の選択肢 SwiftUIではGeometryReaderや.matchedGeometryEffet等でカスタマイズをする https://www.youtube.com/watch?v=2NMjFuiZ5Po SwiftUI Animated Sticky Header: iOS そもそもはComponentとして提供はされていない ※調整に関する計算が煩雑になりがち GeometryReaderを活用した座標計算やScrollViewReader に加え.matchedGeometryEffect等に関する理解が必要

Slide 18

Slide 18 text

まとめ 考え方が異なる点やポイントを押さえておく事で慌てない様にする 必ずしも全ての見え方を合わせる事が正解とは限らない。適材適所でどうするかを考える。 1. 純正のComponentで提供されている要素により注意深く目を向けてみる: Androidでは純正Componentとして提供されているが、iOSではそうではない物もありますし、またはその逆のパターンである場合 もあります。その様な際には別の形で提供する方法や便利なOSS活用も視野に入れると良さそうに思います。 2. Scrollに関するiOS/Android間での違いを押さえておく: 特にScrollに関連する処理については、iOS/Android間で考え方や方針が大きく異なる場合が多いです。特にScrollを利用した複 雑なUI実装を要する場合には、それぞれの特性にうまく合わせた形を模索する必要があると思います。 3. Scroll表現や複雑なUI実装をする画面構造を紐解く楽しさ: 複雑なUI実装に対して向き合う事は決して簡単な事ではありませんが「なぜその様な方針や構成を取るのか?」という点に対し て詳細やポイントになり得る点を見抜く事によって表現の幅が広がっていくと思います。

Slide 19

Slide 19 text

Thank you for listening !