swiftuicontenttransition

How to avoid contentTransition apply black in new text - SwiftUI


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.

enter image description here

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()
}

Solution

  • It helps if you apply .drawingGroup() to the text:

    Text(displayText)
        .font(.callout)
        .contentTransition(.numericText(countsDown: !isRevealed))
        .monospaced()
        .drawingGroup() // 👈 here
    

    Animation


    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)