0

Does anyone know how to achieve strikethrough effect that is constantly displayed in pure TextField ?

Sample code:

@State var textFieldText: String

var body: some View {
    TextField("some_prompt", text: $textFieldText)
        .strikethrough()
}

This compiles without problems, but it looks like this modifier is completely ignored, both in Previews and if you run the "app".

2 Answers 2

0

You could try this simple all SwiftUI approach, drawing a line through the TextField, such as:

struct ContentView: View {
    @State private var text: String = "Strikethrough TextField"

    var body: some View {
        StrikethroughTextField(text: $text)
        .border(.blue)
        .padding()
    }
}

struct StrikethroughTextField: View {
    @Binding var text: String
    
    var body: some View {
        TextField("", text: $text)
            .padding(5)
            .background(
                GeometryReader { geometry in
                    Path { path in
                        let lineY = geometry.size.height / 2
                        path.move(to: CGPoint(x: 0, y: lineY))
                        path.addLine(to: CGPoint(x: geometry.size.width, y: lineY))
                    }
                    .stroke(Color.black, lineWidth: 1)
                }
            )
    }
}

EDIT-1

Another approach is to use a simple AttributedString in an overlay, such as:

struct ContentView: View {
    @State private var textFieldText: String = ""
    
    var striker: AttributedString {
        var result = AttributedString(textFieldText)
        result.font = .body
        result.strikethroughStyle = .single
        return result
    }
    
    var body: some View {
        TextField("prompt", text: $textFieldText)
            .foregroundColor(.clear)
            .overlay(Text(striker).foregroundColor(.black), alignment: .leading)
            .border(.red)
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Nice and simple solution. Thanks !
0

SwiftUI TextFields do not support strikethroughs. You can wrap a UITextField and set its defaultTextAttributes instead.

struct StrikethroughUITextField: UIViewRepresentable {
    @Binding var text: String
    
    init(text: Binding<String>) {
        self._text = text
    }
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.defaultTextAttributes = [
            .strikethroughStyle: NSUnderlineStyle.single.rawValue,
            .strikethroughColor: UIColor.black
        ]
        textField.addTarget(context.coordinator, action: #selector(Coordinator.textDidChange), for: .editingChanged)
        
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        context.coordinator.textCallback = { text = $0 }
        uiView.text = text
    }
    
    func makeCoordinator() -> Coordinator {
        .init()
    }
    
    @MainActor
    class Coordinator: NSObject {
        var textCallback: ((String) -> Void)?
        
        @objc
        func textDidChange(_ textField: UITextField) {
            textCallback?(textField.text ?? "")
        }
        
    }
}

As far as I know, only Texts will be affected by strikethrough(), but you can still apply it to any View. This is by design - this design allows you to apply strikethrough to many Texts, by just writing the modifier once. Under the hood, it's actually just setting a (non-public) EnvironmentValues, i.e. .environment(\.someInternalThing, ...).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.