SwiftUIレイアウト一本勝負に参加してみた

# SwiftUIレイアウト一本勝負

iOSアプリ開発も勉強していきたいなーと思っていたところにピッタリのイベントが始まったので、参加してみることにしました！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/SwiftUI%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E4%B8%80%E6%9C%AC%E5%8B%9D%E8%B2%A0?src=hash&amp;ref_src=twsrc%5Etfw">#SwiftUIレイアウト一本勝負</a> 001<br><br>- サイコロの「5」の目のように「Hello, world!」を配置してください<br>- 画面サイズが小さいデバイスの場合は、上前端（top &amp; leading）と下後端（bottom&amp; trailing）の位置に来るテキストを優先して（=可能な限り改行させないで）表示してください <a href="https://t.co/7N3Asjbiek">pic.twitter.com/7N3Asjbiek</a></p>&mdash; treastrain / Tanaka.R (@treastrain) <a href="https://twitter.com/treastrain/status/1547154171379724289?ref_src=twsrc%5Etfw">July 13, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 初めに思いついた実装

まず、VStack, HStack, Spacerを使ってサイコロの5の目のレイアウトを作りました。
また、top&leading, bottom&trailingな位置のTextに対してフレームを使って幅を与え、改行に対応しました。

```swift
import SwiftUI

struct ContentView: View {
    
    let word: String = "Hello, world!"
    let width: CGFloat = 100.0
    
    var body: some View {
        VStack {
            HStack {
                Text(word).frame(width: width)
                Spacer()
                Text(word)
            }
            Spacer()
            Text(word)
            Spacer()
            HStack {
                Text(word)
                Spacer()
                Text(word).frame(width: width)
            }
        }.padding()
    }
}
```
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/9f545404-694d-edfe-60e5-afed05ae625e.png"><img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/714bb4bb-bf72-82b2-51b3-d21743a3b99e.png">

# 問題点
上の実装では、フレームの幅を明示的に決めていることによる不具合が考えられます。

### ①文字数の変化に対応できない
表示文字列を増やしてみると、Textの幅が100を超えてしまい意図していないTextまで改行されてしまいます。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/66a2226c-aa5f-1107-6aab-42de82f10a72.png">


### ②いい感じのフレーム幅を探す必要がある
フレーム幅を150にしてみると、余分なスペースを確保してしまっていい感じになりません。
いい感じのフレーム幅を地道に探す手間がかかってしまいます。もっといい方法がありそうです。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/a541c43d-42a1-711c-2d95-20fea37f71f0.png">

## 解決策
### 柔軟な実装にしたい！
ドキュメントでも、なるべく柔軟な対応のできるレイアウト実装を呼びかけています。

> only consider this when you can’t achieve your desired layout in an adaptive, flexible way.
> 引用:　[Building Layouts with Stack Views | Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/building-layouts-with-stack-views)

優先するTextの分の幅を先に確保して、余った範囲に残りのTextを収めてもらうような実装がしたいです。

### fixedSize()
fixedSizeを使えば、文字列に合わせて高さと幅を確保する事ができそうです。
> The fixedSize() modifier can be used to create a view that maintains the ideal size of its children both dimensions
> 引用:　[fixedSize() | Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/link/fixedsize())


### layoutPriority()
layoutPriorityを使うと、priorityの値が大きいものから優先してサイズを割り振るようです。
> Raising a view’s layout priority encourages the higher priority view to shrink later when the group is shrunk and stretch sooner when the group is stretched.
> [layoutPriority(_:) | Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/path/layoutpriority(_:))

(実用的に使いやすい場面が多そうだなーと思うなどしました)

### 比較
二つの手法の違いは、画面サイズを超える文字数にしたときに現れました。
layoutPriorityでは優先度の高いものであっても改行することで画面に収めようとします。
それに対してfixedSizeでは画面サイズを超えても改行せず、画面を貫通しています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/3a25e25f-9505-87f8-2417-949e6be3d32c.png">

# 結論
今回は、お題に
> ──の位置に来るテキストを優先して（=可能な限り改行させないで）表示してください

「可能な限り」ということで、不可能な時は改行できるlayoutPrioriryがより意図に合っていそうだと考えました。
```swift
import SwiftUI

struct ContentView: View {
    
    let word: String = "Hello, world!"
    let priority: CGFloat = 1.0
    
    var body: some View {
        VStack {
            HStack {
                Text(word).layoutPriority(priority)
                Spacer()
                Text(word)
            }
            Spacer()
            Text(word)
            Spacer()
            HStack {
                Text(word)
                Spacer()
                Text(word).layoutPriority(priority)
            }
        }.padding()
    }
}
```
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/72668c45-95e0-3df0-2dda-057b2d91dd4a.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/57bcd160-efee-7bdf-5f26-1619b8218531.png">


優先するTextの幅を先に確保しておく実装ができました！
表示文字列の長さによって優先するTextの幅が変わっているのが分かります。

# まとめ
VStack, HStack, Spacerを使って大まかなレイアウトを作りました。
改行をなるべくさせないための実装、特に固定値を避けるための方法を比較しました。

iOSアプリ開発、特にSwiftUIに手を付けるきっかけになるイベントでした！引き続き参加しようと思います🔥🔥
たまにブログにもまとめられたらいいなー。