LoginSignup
17
9

【iOS16で簡単になった】SwiftUI.Textで固定行数分の高さを確保する方法

Posted at

はじめに

以下のようなグリッドレイアウトでテキストの仕様が以下のように決められているとき、どのように実装すれば良いでしょうか?

  • テキストはどのようなものが来るかわからない(長いかもしれないし短いかもしれない)
  • テキストは2行分の高さを確保し、左上寄せで表示する
  • 2行でおさまらない長さのときは末尾に...を表示する

image.png

サンプルコード

実装全体はこんな感じです。
この中で、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行分の高さしか確保してくれません。
このため、以下のようにレイアウトが崩れてしまいます。

Cursor.png

この場合、minHeightを指定してあげることで仕様通りのレイアウトを実装することができます。
しかし、フォントサイズやlineSpacing等の値によって2行分のテキストのフレームサイズは変わるため、デザインとぴったり合わせるのに結構苦労します。

Text(text)
    .font(.system(size: 12))
    .lineLimit(2)
    .frame(maxWidth: .infinity, minHeight: 28.66, alignment: .topLeading)

iOS16の実装

iOS16からlineLimit(_:reservesSpace:)が使えるようになりました。

reservesSpacetrueにすることで、指定した行数分の高さを確保してくれます。これは便利ですね。

Text(text)
    .font(.system(size: 12))
    .lineLimit(2, reservesSpace: true)
    .frame(maxWidth: .infinity, alignment: .topLeading)
17
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
9