0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事はSwiftを勉強する過程で書き溜めたものを、もったいないかも精神で投稿しています。上手くいかなかったり、他にスマートな方法があるかもしれません。

はじめに

表示するテキストが指定した行数を超えると、途中で省略して「続きを表示」ボタンを出します。SNS系のアプリでよく見るやつです。

output-palette.gif

参考元

SwiftUIでTruncated Text|TAAT
SwiftUI: How to make see more see less style butto

参考元様との違いは「続きを表示」がテキスト中に表示されるか、次の行に表示するかが異なります。
今回は「X」のまんまを実装したかったので、シンプルに後者で実装しました。

確認した環境

Xcode 16.0
iOS 18.0

考え方

テキストの省略表示自体はlineLimitモディファイアで実現できます。「続きを表示」を表示するか否かをlineLimitを設定しないパターンと比較して制御します。

具体的な流れは、下記のとおりです。

  1. lineLimitを使って表示する行数を指定
  2. 制限せずにテキストを表示した場合の高さを、1.のbackground非表示で仕込む
  3. 1.と2.を比較して相違がある場合、テキストが省略されていると判断して「続きを表示」ボタンを表示

名称未設定ファイル.drawio.png

コード

struct ExpandableTextView: View {
    @State private var isExpanded = false // テキストが展開されているかどうかの状態
    @State private var isTruncated = false // テキストが制限行数を超えているかの状態

    let text: String
    let lineLimit: Int // 制限行数

    var body: some View {
        VStack(alignment: .leading) {
            // テキスト表示部分
            Text(text)
                .lineLimit(isExpanded ? nil : lineLimit)
                .background(
                    // 「続きを読む」用にテキストを制限パターンと無制限パターンを非表示で生成して高さを比較する
                    Text(text) // A.指定行数制限されるテキストを設置して、サイズを計測
                        .lineLimit(lineLimit)
                        .background(GeometryReader { limitTextGeometry in
                            ZStack {
                                Text(text) // B.全表示されるテキストを設置し、サイズを計測
                                    .background(GeometryReader { fullTextGeometry in
                                        Color.clear.onAppear {
                                            // BとAを比較してB(全表示)が大きい場合は省略されていると判定
                                            isTruncated = fullTextGeometry.size.height > limitTextGeometry.size.height
                                        }
                                    })
                            }
                            .frame(height: .greatestFiniteMagnitude) // 子が親の高さに依存しない
                        })
                        .hidden() // 計算用に使ったテキストは非表示にする
                )

            // 「続きを表示」または「閉じる」ボタン
            if isTruncated { // テキストが制限行数を超えている場合にのみ表示
                Button(action: {
                    isExpanded.toggle()
                }) {
                    Text(isExpanded ? "閉じる" : "続きを表示")
                        .foregroundColor(.blue)
                }
            }
        }
    }
}
0
2
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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?