taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit a92dc23dcc3861ac0fbec1ea2ecf0d1d727ae5da
parent e5429229270cbdf6b42d0db9271881c9b78cf15b
Author: Marc Stibane <marc@taler.net>
Date:   Tue,  8 Jul 2025 17:32:45 +0200

cleanup

Diffstat:
MTalerWallet1/Views/HelperViews/TruncationDetectingText.swift | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
MTalerWallet1/Views/Transactions/TransactionRowView.swift | 57+++++++++++++++++++++++++++++----------------------------
2 files changed, 95 insertions(+), 59 deletions(-)

diff --git a/TalerWallet1/Views/HelperViews/TruncationDetectingText.swift b/TalerWallet1/Views/HelperViews/TruncationDetectingText.swift @@ -42,30 +42,7 @@ struct TruncationDetectingText: View { private let index: Int private let strikeColor: Color? - @State private var actualSize: CGSize = .zero - @State private var naturalHeightSize: CGSize = .zero - @State private var naturalWidthSize: CGSize = .zero - - /// Indicates whether the text content is truncated under current constraints - private var isContentTruncated: Bool? { - guard actualSize != .zero, - naturalWidthSize != .zero, - naturalHeightSize != .zero - else { - return nil - } - - switch maxLines { - case .none: - return false - case 1: - // For single line: check if natural width exceeds actual width - return naturalWidthSize.width > actualSize.width - default: - // For multiple lines: check if natural height exceeds actual height - return naturalHeightSize.height > actualSize.height - } - } + @StateObject var sizeHolder: SizeHolder // MARK: - Initialization @@ -82,25 +59,26 @@ struct TruncationDetectingText: View { self.maxLines = (maxLines ?? 0) > 0 ? maxLines : nil self.strikeColor = strikeColor + self._sizeHolder = StateObject(wrappedValue: SizeHolder(maxLine: maxLines)) } // MARK: - Body var body: some View { constrainedText - .measureSize($actualSize) + .measureSize(sizeHolder.binding(keyPath: \.actualSize)) .background { naturalHeightText - .measureSize($naturalHeightSize) + .measureSize(sizeHolder.binding(keyPath: \.naturalHeightSize)) .hidden() } .background { naturalWidthText - .measureSize($naturalWidthSize) + .measureSize(sizeHolder.binding(keyPath: \.naturalWidthSize)) .hidden() } .overlay { - if let isContentTruncated = isContentTruncated { + if let isContentTruncated = sizeHolder.isContentTruncated { let value = [layout: isContentTruncated] switch index { case 0: @@ -118,9 +96,9 @@ struct TruncationDetectingText: View { } } } -#if DEBUG - .task(id: isContentTruncated) { - if let isContentTruncated = isContentTruncated { +#if DEBUG2 + .task(id: sizeHolder.isContentTruncated) { + if let isContentTruncated = sizeHolder.isContentTruncated { print("Layout \(layout) - Content truncated: \(isContentTruncated)") } } @@ -212,3 +190,60 @@ extension View { }) } } +// MARK: - SizeHolder + +@MainActor +final class SizeHolder: ObservableObject { + var actualSize: CGSize = .zero { + didSet { + needsUpdate() + } + } + + var naturalHeightSize: CGSize = .zero { + didSet { + needsUpdate() + } + } + + var naturalWidthSize: CGSize = .zero { + didSet { + needsUpdate() + } + } + + var maxLines: Int? + + init(maxLine: Int? = nil) { + maxLines = maxLine + } + + // Indicates whether the text content is truncated under current constraints + @Published var isContentTruncated: Bool? + + func needsUpdate() { + guard actualSize != .zero, + naturalWidthSize != .zero, + naturalHeightSize != .zero + else { + return + } + + switch maxLines { + case .none: + isContentTruncated = false + case 1: + // For single line: check if natural width exceeds actual width + isContentTruncated = naturalWidthSize.width > actualSize.width + default: + // For multiple lines: check if natural height exceeds actual height + isContentTruncated = naturalHeightSize.height > actualSize.height + } + } + + func binding(keyPath: ReferenceWritableKeyPath<SizeHolder, CGSize>) -> Binding<CGSize> { + Binding( + get: { self[keyPath: keyPath] }, + set: { self[keyPath: keyPath] = $0 }) + } +} diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift b/TalerWallet1/Views/Transactions/TransactionRowView.swift @@ -27,6 +27,7 @@ struct TransactionTimeline: View { } } +@MainActor struct TransactionRowView: View { private let symLog = SymLogV(0) let scope: ScopeInfo @@ -46,9 +47,9 @@ struct TransactionRowView: View { let keys1 = layoutStati1.keys.sorted(by: { $0 < $1 }) for key in keys0 { - let val0 = layoutStati0[key] ?? true - let val1 = layoutStati1[key] ?? true - if !val0 && !val1 { + let isTruncated0 = layoutStati0[key] ?? true + let isTruncated1 = layoutStati1[key] ?? true + if !isTruncated0 && !isTruncated1 { return key } } @@ -149,6 +150,9 @@ struct TransactionRowView: View { .foregroundColor(textColor) .talerFont(.headline) .padding(.bottom, -2.0) +#if DEBUG + .border(red) +#endif .accessibilityLabel(a11yLabel) } TransactionTimeline(timestamp: common.timestamp, textColor: textColor, layout: 0, maxLines: 1) @@ -170,6 +174,9 @@ struct TransactionRowView: View { .foregroundColor(textColor) .talerFont(.headline) .padding(.bottom, -2.0) +#if DEBUG + .border(red) +#endif .accessibilityLabel(a11yLabel) } HStack(spacing: 6) { @@ -180,12 +187,11 @@ struct TransactionRowView: View { .background(green.opacity(0.2)) #endif } -// .border(blue) } let layout2 = VStack(alignment: .leading, spacing: 0) { if let topString { // top & amount, bottom below - HStack(spacing: 8) { + HStack(spacing: 6) { TruncationDetectingText(topString, maxLines: 1, layout: 2, @@ -194,7 +200,9 @@ struct TransactionRowView: View { .talerFont(.headline) .padding(.bottom, -2.0) .accessibilityLabel(a11yLabel) -// .border(blue) +#if DEBUG +// .border(red) +#endif Spacer(minLength: 2) amountV #if DEBUG @@ -203,7 +211,7 @@ struct TransactionRowView: View { } TransactionTimeline(timestamp: common.timestamp, textColor: textColor, layout: 2, maxLines: 10) } else { // no top, bottom & amount - HStack(spacing: 8) { + HStack(spacing: 6) { TransactionTimeline(timestamp: common.timestamp, textColor: textColor, layout: 2, maxLines: 10) Spacer(minLength: 2) amountV @@ -214,7 +222,7 @@ struct TransactionRowView: View { } } - let layout3 = VStack(alignment: .leading, spacing: 2) { // top full-width, amount right, bottom full-width + let layout3 = VStack(alignment: .leading, spacing: 2) { // top full-width, amount trailing, bottom full-width if let topString { TruncationDetectingText(topString, maxLines: 10, @@ -225,11 +233,14 @@ struct TransactionRowView: View { .talerFont(.headline) // .fontWeight(.medium) iOS 16 only .padding(.bottom, -2.0) +#if DEBUG + .border(red) +#endif .accessibilityLabel(a11yLabel) } HStack(spacing: -4) { - Spacer(minLength: 2) + Spacer(minLength: 0) amountV #if DEBUG .background(blue.opacity(0.2)) @@ -240,37 +251,27 @@ struct TransactionRowView: View { HStack { iconBadge -// .border(blue) +#if DEBUG + .border(blue) +#endif ZStack { layout0 .layoutPriority(isLayoutSelected(0) ? 2 : 1) .opacity(isLayoutSelected(0) ? 1 : 0) -#if DEBUG - .border(orange) -#endif layout1 .layoutPriority(isLayoutSelected(1) ? 2 : 1) .opacity(isLayoutSelected(1) ? 1 : 0) -#if DEBUG - .border(green) -#endif layout2 .layoutPriority(isLayoutSelected(2) ? 2 : 1) .opacity(isLayoutSelected(2) ? 1 : 0) -#if DEBUG -// .border(red) -#endif - layout3 - .layoutPriority(isLayoutSelected(3) ? 2 : 1) - .opacity(isLayoutSelected(3) ? 1 : 0) -#if DEBUG - .border(blue) -#endif - } - .onPreferenceChange(LayoutTruncationStatus0.self) { stati in + layout3 + .layoutPriority(isLayoutSelected(3) ? 2 : 1) + .opacity(isLayoutSelected(3) ? 1 : 0) + } + .onPreferenceChange(LayoutTruncationStatus0.self) { stati in // top string self.layoutStati0 = stati } - .onPreferenceChange(LayoutTruncationStatus1.self) { stati in + .onPreferenceChange(LayoutTruncationStatus1.self) { stati in // Timeline self.layoutStati1 = stati } }