Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOS/Android間でUI実装を近づけるヒントと道標

Fumiya Sakai
January 28, 2025
160

 iOS/Android間でUI実装を近づけるヒントと道標

potatotips#90での登壇資料になります。

Androidと同じ形でいけそうに見えるが実はそうではない?なUI実装事例を紹介しています。
本資料では「1.テキスト入力に関する事例」「2.Chipを使った表現事例」「3.Scrollが関連する表現事例」の3点を題材にして、意外と類似していそうで、実際はそうではない点がある事に注意が必要なものに関する解説をしています。

純正のComponentで提供されている要素により注意深く目を向け、Scrollに関するiOS/Android間での違いを押さえるという2点がポイントになると考えています。

Fumiya Sakai

January 28, 2025
Tweet

More Decks by Fumiya Sakai

Transcript

  1. 自己紹介 ・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
  2. 改行や長文テキスト入力に関する事例 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行分まで伸縮する
  3. @Composable fun FliterChipEachRow( chipList: List<String>, tempList: Set<Int> ) { 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として準備されています。 デザインに合わせたある程度のカスタマイズ もする事ができます。
  4. 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要素)を組み合わせる事によって実現可能である
  5. 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要素は残り続ける
  6. 類似した表現をSwiftUIで作成する際の構成 DroidKaigi2023で実装されていた事例を応用して作成したサンプルになります LazyVStackと配置したSection要素やUI構造を工夫する必要がある: λΠτϧ هࣄҰཡ χϡʔε هࣄҰཡ χϡʔε λΠτϧ https://github.com/fumiyasac/LikeCoodinatorLayoutExample

    SectionHeader要素のボタ ン背景に対するAnimation はGeometryReaderを利用し て実施する。 Offset値を監視してその値を 引き渡す。@Stateの変化を利 用して高さを変更する。 SwiftUIで作成する場合は、 LazyVStackを利用して作成す る点がポイントになる。
  7. 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速度の反映