SwiftUI: UI写経のススメ-UISegmentedControl

p0dee
4 min readMay 25, 2020

近頃、昨年の発表直後にApple公式のチュートリアルを触って以来久しぶりに、本格的にSwiftUIを触り始めた。

趣味プログラミングはさておいて、SwiftUIを実案件に導入することを考えた際、まず気になったのは「実際の所どれほどの表現力があるのか」であった。そこで、上述した公式チュートリアルや、そのほかの書籍を読みながら試行錯誤していくのだが、、

そんな中で気がついた事は、UIKitのコンポーネントをSwiftUIで再現することによって、SwiftUIへの理解が加速し、加えて(現状の)SwiftUIに「できないこと」を炙り出せるのではないか、ということだった。

最終目標が常に明確でありながら、UIKitで実装されているがゆえにSwiftUIでは一筋縄に行かない、つまり試行錯誤を要するという点では、多角的かつ効率的に理解を深められると実感しはじめている。

そこで、UIKitのコンポーネントをいくつか例にとって、SwiftUIによる「写経」を試み、SwiftUIにおける表現力向上に挑戦してみたいと思う。

今回は、UISegmentedControl。iOS 13以降デザインが一新されたコンポーネントのひとつで、従来の簡素さから表現力がおおきく飛躍した。

そして出来上がったのがこちら。

この実装における、いくつかのポイントを紹介してみる。(投稿の最後に実装全体へのリンクも貼り付けた)

  1. ドラッグジェスチャ(drag gesture)を扱う

ドラッグジェスチャ(drag gesture)の現在位置のx座標を、項目ごとの幅(cellWidth)で割ることで、何番目の項目が選択されているか(index)を計算することができる。

  • 項目ごとの幅 = 全体の幅 / 項目数
  • 選択インデックス = 現在位置x / 項目ごとの幅

しかしここでのつまずき所が全体の幅 、すなわちSegmentedControl自体の幅である。UIKitのようなself.frameが提供されていないSwiftUIでは、こうした自明に思われる幾何情報すら取得に難儀するのである。

そこで活躍するのがGeometryReader。ここから得た値を、自身のプロパティ@State var width: CGFloatに保持するようにした。

PreferenceKeyで監視した bounds の変化に応じて、GeometryReaderで得た geometryProxy を任意に取り扱う処理を、extension Viewとして実装し共通化した。

たかだか自身のサイズを取得するためだけに、ここまで大仰な実装が必要なのは、かなり冗長に感じる、、より簡潔に実装する方法があれば知りたいのだが。

2. 階層内の他ビューの幾何情報を扱う

SegmentedControlにおいて、選択中の項目をハイライトするためには、その項目のframeを知る必要があるのだが、前述の通り、SwiftUIにおいてはUIKitのように都合良くはいかない。

そこで、AnchorPreferenceの仕組みを利用して、選択済み項目のboundsを上位階層に伝播する。

ここからは、今回の実装で気がついた、投稿時点のSwiftUIではどうしても取り扱えなかった仕様について記す。その大きなひとつとして

  • タッチダウンジェスチャを検知できない

というものが挙げられる。これにより、タッチダウン時の視覚効果はSwiftUIのみでは再現することができなかった。

※ SwiftUIのみに拘らなければ、UIViewPresentableを活用する方法がある。

以下に貼り付ける実装の全容は、試行錯誤を経てようやくたどり着いたひとつの答えなのだが、よりシンプルに実装する方法はありそうだ(寧ろあって欲しい)。

いずれにしてもここで伝えたかったことは、あえて非SwiftUI的な実装目標を置くことにより、SwiftUIで何ができ、何ができないのかを知る一助になった、ということだ。それは標準コンポーネントでも良いし、3rdパーティアプリを真似てみるのでも良さそうだ。

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response