Since iOS 27, if an app runs in Arabic (or, presumably, any right-to-left language), typing digits or Latin letters into a SwiftUI TextField makes the field look empty. The binding updates and the caret shows, but the text isn't drawn until editing ends.
What I measured:
The text's layout fragment is placed at about x = −39,655 pt in a 370 pt wide field, so it's drawn far outside the field.
A plain TextField and one with .environment(\.layoutDirection, .leftToRight) (phone and card numbers) are both affected.
A UIKit UITextField on the same screen isn't affected.
The same build on iOS 18.0 works, and iOS 27 in English works.
Minimal repro: one SwiftUI file with four fields. Run it with App Language set to Arabic, tap the first field and type 523. Launch with -autoProbe to have it type into each field and print where the text was laid out.
PROBE iOS 27.0, language ar, layout RTL
PROBE field 1 text="523" fieldWidth=370 fragmentX=[-39655] visible=false // SwiftUI TextField
PROBE field 2 text="523" fieldWidth=370 fragmentX=[-39655] visible=false // SwiftUI, LTR pinned
PROBE field 3 text="523" fieldWidth=370 fragmentX=[314] visible=true // UIKit UITextFieldWorkaround that works for us, installed once at launch:
import UIKit
enum TextFieldDirection {
static func install() {
for name in [UITextField.textDidBeginEditingNotification, UITextField.textDidChangeNotification] {
NotificationCenter.default.addObserver(forName: name, object: nil, queue: .main) { note in
guard let field = note.object as? UITextField else { return }
MainActor.assumeIsolated { settle(field) }
// SwiftUI rebuilds the paragraph style right after; settle again.
DispatchQueue.main.async { MainActor.assumeIsolated { settle(field) } }
}
}
}
@MainActor static func settle(_ field: UITextField) {
let direction: NSWritingDirection =
field.effectiveUserInterfaceLayoutDirection == .rightToLeft ? .rightToLeft : .leftToRight
if let whole = field.textRange(from: field.beginningOfDocument, to: field.endOfDocument) {
field.setBaseWritingDirection(direction, for: whole)
}
func directed(_ attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] {
var attributes = attributes
let style = (attributes[.paragraphStyle] as? NSParagraphStyle)?.mutableCopy() as? NSMutableParagraphStyle
?? NSMutableParagraphStyle()
style.baseWritingDirection = direction
attributes[.paragraphStyle] = style
return attributes
}
if let typing = field.typingAttributes { field.typingAttributes = directed(typing) }
field.defaultTextAttributes = directed(field.defaultTextAttributes)
field.setNeedsLayout()
field.layoutIfNeeded()
}
}Don't pin textAlignment instead. SwiftUI puts .natural back on every keystroke and the caret jumps from side to side.
If you see this too, please file a duplicate referencing FB25410049. Duplicates help Apple prioritise it.
1
0
29