I'm experiencing a minor issue. When I finish replacing the text, it appears a bit blacker than the rest of the text. I don't know how to fix this, or if it's a SwiftUI bug.
extension String {
func formatWithMask(_ mask: String) -> String {
let cleanNumber = components(separatedBy: CharacterSet.alphanumerics.inverted).joined()
var result = ""
var startIndex = cleanNumber.startIndex
let endIndex = cleanNumber.endIndex
for char in mask where startIndex < endIndex {
if char == "#" {
result.append(cleanNumber[startIndex])
startIndex = cleanNumber.index(after: startIndex)
} else {
result.append(char)
}
}
return result
}
}
extension String {
func hideCharacters(leadingDiscard: Int = 0, trailingDiscard: Int = 4, charHide: String = "•") -> String {
let length = self.count
guard length > leadingDiscard + trailingDiscard else {
return self
}
let start = self.index(self.startIndex, offsetBy: leadingDiscard)
let end = self.index(self.endIndex, offsetBy: -trailingDiscard)
let leadingPart = self[..<start]
let trailingPart = self[end..<self.endIndex]
var hiddenPart = ""
for char in self[start..<end] {
if char.isWhitespace || !char.isLetter && !char.isNumber {
hiddenPart.append(char)
} else {
hiddenPart.append(charHide)
}
}
return leadingPart + hiddenPart + trailingPart
}
}
RevealText
struct RevealText: View {
var text = "4916956785082510"
// State to track whether the credit card is revealed or hidden
@State private var isRevealed = false
var body: some View {
VStack {
HStack {
// Format the credit card number with a mask
let formattedText = text.formatWithMask("#### #### #### ####")
// Hide characters if not revealed
let displayText = isRevealed ? formattedText : formattedText.hideCharacters(leadingDiscard: 4, trailingDiscard: 4)
Text(displayText)
.font(.callout)
.contentTransition(.numericText(countsDown: !isRevealed))
.monospaced()
// Button to toggle reveal state
Button("Reveal code", systemImage: "eye") {
withAnimation {
isRevealed.toggle()
}
}
.labelStyle(.iconOnly)
.buttonStyle(.bordered)
.controlSize(.small)
.symbolVariant(isRevealed ? .slash : .none)
}
}
.padding()
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8))
}
}
#Preview {
RevealText()
}
It helps if you apply .drawingGroup()
to the text:
Text(displayText)
.font(.callout)
.contentTransition(.numericText(countsDown: !isRevealed))
.monospaced()
.drawingGroup() // 👈 here
Alternatively, the environment value contentTransitionAddsDrawingGroup
can be used to determine whether content transitions should be wrapped as a drawing group. This might be useful if the content transition is embedded inside a custom component:
RevealText()
.environment(\.contentTransitionAddsDrawingGroup, true)