はじめに
以下のようなグリッドレイアウトでテキストの仕様が以下のように決められているとき、どのように実装すれば良いでしょうか?
- テキストはどのようなものが来るかわからない(長いかもしれないし短いかもしれない)
- テキストは2行分の高さを確保し、左上寄せで表示する
- 2行でおさまらない長さのときは末尾に
...
を表示する
サンプルコード
実装全体はこんな感じです。
この中で、Textビューの実装に着目します。
struct ContentView: View {
var body: some View {
VStack {
HStack {
item(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit")
item(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit")
}
HStack {
item(text: "Lorem ipsum")
item(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit")
}
HStack {
item(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt")
item(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit")
}
}
.padding(16)
}
func item(text: String) -> some View {
VStack {
RoundedRectangle(cornerRadius: 4)
.frame(maxWidth: .infinity)
.frame(height: 100)
.foregroundColor(.gray)
.overlay {
Text("画像")
.foregroundStyle(Color.white)
.font(.system(size: 14))
}
Text(text)
.font(.system(size: 12))
.lineLimit(2)
.frame(maxWidth: .infinity, alignment: .topLeading)
}
}
}
iOS15の実装
テキストの行数制限を指定するAPIとしてlineLimit(_:)
が用意されています。
Text(text)
.font(.system(size: 12))
.lineLimit(2)
.frame(maxWidth: .infinity, alignment: .topLeading)
lineLimit(_:)
を使うことで長いテキストを2行におさめることはできるのですが、テキストが1行分しかない場合は1行分の高さしか確保してくれません。
このため、以下のようにレイアウトが崩れてしまいます。
この場合、minHeight
を指定してあげることで仕様通りのレイアウトを実装することができます。
しかし、フォントサイズやlineSpacing等の値によって2行分のテキストのフレームサイズは変わるため、デザインとぴったり合わせるのに結構苦労します。
Text(text)
.font(.system(size: 12))
.lineLimit(2)
.frame(maxWidth: .infinity, minHeight: 28.66, alignment: .topLeading)
iOS16の実装
iOS16からlineLimit(_:reservesSpace:)
が使えるようになりました。
reservesSpace
をtrue
にすることで、指定した行数分の高さを確保してくれます。これは便利ですね。
Text(text)
.font(.system(size: 12))
.lineLimit(2, reservesSpace: true)
.frame(maxWidth: .infinity, alignment: .topLeading)