Skip to content

Commit

Permalink
fix: テキスト内で短い改行があると、必要以上に省略表示される問題を修正
Browse files Browse the repository at this point in the history
  • Loading branch information
TAATHub committed Jun 18, 2023
1 parent 3a6243a commit d1e7989
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 43 deletions.
26 changes: 18 additions & 8 deletions TruncatedText/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import SwiftUI

struct ContentView: View {
let text = "吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た"
let text = "吾輩は猫である。\n名前はまだ無い。\nどこで生れたかとんと見当がつかぬ。\n何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。\n吾輩はここで始めて人間というものを見た"

var body: some View {
VStack(spacing: 40) {
Text(text)
.lineLimit(3)
GeometryReader { proxy in
VStack(alignment: .leading, spacing: 40) {
Text(text)
.lineLimit(2)

// 内部のGeometryProxyでサイズを計測する
TrancatedText(text,
lineLimit: 2,
ellipsis: .init(text: "More", color: .blue))

TrancatedText(text,
lineLimit: 3,
ellipsis: .init(text: "More", color: .blue))
// GeometryProxyを渡して、親Viewのwidthを使ってサイズを計測する
TrancatedText(text,
lineLimit: 2,
ellipsis: .init(text: "More", color: .blue),
proxy: proxy)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
}
.padding()
}
}

Expand Down
78 changes: 43 additions & 35 deletions TruncatedText/TruncatedText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct TrancatedText: View {
let lineSpacing: CGFloat
let font: UIFont
let ellipsis: Ellipsis
let proxy: GeometryProxy?

private var text: String
private var ellipsisPrefixText: String {
Expand All @@ -37,12 +38,14 @@ struct TrancatedText: View {
lineLimit: Int,
lineSpacing: CGFloat = 2,
font: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body),
ellipsis: Ellipsis = Ellipsis()) {
ellipsis: Ellipsis = Ellipsis(),
proxy: GeometryProxy? = nil) {
self.text = text
self.lineLimit = lineLimit
self.lineSpacing = lineSpacing
self.font = font
self.ellipsis = ellipsis
self.proxy = proxy

_truncatedText = State(wrappedValue: text)
}
Expand All @@ -68,44 +71,49 @@ struct TrancatedText: View {
.background(GeometryReader { visibleTextGeometry in
Color.clear
.onAppear {
let size = CGSize(width: visibleTextGeometry.size.width, height: .greatestFiniteMagnitude)
let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: font]
searchTruncatedText(width: proxy?.size.width ?? visibleTextGeometry.size.width,
height: visibleTextGeometry.size.height)
}
}))
.font(Font(font))
}

private func searchTruncatedText(width: CGFloat, height: CGFloat) {
let size = CGSize(width: width, height: .greatestFiniteMagnitude)
let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: font]

// 二分探索で省略テキストを更新する
// 終了条件: mid == low && mid == high
var low = 0
var heigh = truncatedText.count
var mid = heigh
// 二分探索で省略テキストを更新する
// 終了条件: mid == low && mid == high
var low = 0
var heigh = truncatedText.count
var mid = heigh

while (heigh - low) > 1 {
// 固定幅に対するテキストの高さを取得するためにNSAttributedStringを用いる
let attributedText = NSAttributedString(
string: truncatedText + ellipsisPrefixText + ellipsisText,
attributes: attributes)
let boundRect = attributedText.boundingRect(
with: size,
options: NSStringDrawingOptions.usesLineFragmentOrigin,
context: nil)
while (heigh - low) > 1 {
// 固定幅に対するテキストの高さを取得するためにNSAttributedStringを用いる
let attributedText = NSAttributedString(
string: truncatedText + ellipsisPrefixText + ellipsisText,
attributes: attributes)
let boundRect = attributedText.boundingRect(
with: size,
options: NSStringDrawingOptions.usesLineFragmentOrigin,
context: nil)

if boundRect.size.height > visibleTextGeometry.size.height {
truncated = true
heigh = mid
mid = (heigh + low) / 2
} else {
if mid == text.count {
break
} else {
low = mid
mid = (low + heigh) / 2
}
}
if boundRect.size.height > height {
truncated = true
heigh = mid
mid = (heigh + low) / 2
} else {
if mid == text.count {
break
} else {
low = mid
mid = (low + heigh) / 2
}
}

// truncatedTextの更新による再描画はSwiftUIが管理しており、この二分探索の処理による再描画は最終更新後に行われた
truncatedText = String(text.prefix(mid))
}
}
}))
.font(Font(font))
// truncatedTextの更新による再描画はSwiftUIが管理しており、この二分探索の処理による再描画は最終更新後に行われた
truncatedText = String(text.prefix(mid))
}
}
}

Expand Down

0 comments on commit d1e7989

Please sign in to comment.