Published: Sep 19, 2024
Happy iOS 18 release week! Exciting to see all of the new dark mode app icons going live; congrats to all of the indie devs out there who’ve gotten their apps approved in time for launch!
In testing iOS 18 on my own apps, I noticed that text selection no longer works in SwiftUI. This behavior is unfortunately reproducible on both the iOS 18 and iOS 18.1 tracks. Here’s the repro case:
import SwiftUI struct ContentView: View { var body: some View { List { Text("Hello, world!") .textSelection(.enabled) } } }
What this is supposed to look like when you tap and hold:
Unfortunately the tap and hold gesture appears to have broken in iOS 18. I filed FB15178192 to track this and will update this thread if/when the issue is resolved.
#
WorkaroundIn the meantime, I needed a workaround for this because the text selection behavior is an important use case in Sidecar to copy vehicle identification numbers (VINs).
Building a generic solution that works as a drop-in replacement for .textSelection(.enabled)
is a bit tough, because we can’t easily get access to the modified view’s text contents. An alternative workaround is to provide a SelectableText
view that allows us to pass the text string to the clipboard when needed.
import SwiftUI struct SelectableText: View { private let string: String init(_ string: String) { self.string = string } init<F>(_ input: F.FormatInput, format: F) where F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { self.string = format.format(input) } var body: some View { #if !os(watchOS) if #available(iOS 18, *) { Text(string) .contextMenu { Button(action: { UIPasteboard.general.string = string }) { Label("Copy", systemImage: "doc.on.doc") } } } else { Text(string) .textSelection(.enabled) } #else Text(string) #endif } }
Our code can now go from this:
import SwiftUI struct ContentView: View { var body: some View { List { Text("Hello, world!") .textSelection(.enabled) } } }
to this:
import SwiftUI struct ContentView: View { var body: some View { List { SelectableText("Hello, world!") } } }
If/when iOS 18 fixes this behavior we can add additional checks to fall back to the system copy menu again.