SwiftUI View Preview Selecting Issue
If you have ever tried to select an element in the preview (Canvas) of your SwiftUI view, you may have noticed that sometimes it doesn't get selected. Instead, the whole simulator window gets selected, and the Attributes Inspector reads “Multiple Selection”. This can be a frustrating issue when you are trying to debug or modify your code.
A preview of a SwiftUI view when the user tried to select the internal Text view but it didn’t work
Reason One
In some cases, the culprit may be creating a new return variable in the reduce (map?) closure. For example, the following code would cause the issue:
emotions.reduce(Text("")) { result, emotion in
var result = result
if emotion != emotions.first {
result = result + Text(", ")
}
return result + Text(emotion)
}
To avoid this issue, we can modify the code to return just a modification of the initial result, without creating a new variable. The code for our sample case that doesn’t break the preview may look like this:
emotions.reduce(Text("")) { result, emotion in
result
+ Text(emotion == emotions.first ? "" : ", ")
+ Text(emotion)
}
Another workaround is to move the code as-is into a separate function:
struct TestView: View {
let emotions: [String]
var body: some View {
emotions.reduce(Text("")) { result, emotion in
self.buildText(result: result, emotion: emotion, isFirst: emotion == emotions.first)
}
}
private func buildText(result: Text, emotion: String, isFirst: Bool) -> Text {
var result = result
if !isFirst {
result = result + Text(", ")
}
return result + Text(emotion)
}
}
The full code of the troubling case:
import SwiftUI
struct TestView: View {
let emotions: [String]
var body: some View {
emotions.reduce(Text("")) { result, emotion in
var result = result
if emotion != emotions.first {
result = result + Text(", ")
}
return result + Text(emotion)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView(emotions: ["antsy", "anxious"])
}
}
Why so complex reduce with Texts?
Text
s, not String
s, to get advantage of the LocalizedStringKey
type. All of the strings that goes to its constructor are automatically exported with the Product → Export Localizations menu. When dealing with the LocalizedString
API, we need to manually add these string to the localization files. In the real-world code the emotion is not a string but rather an enum that returns a LocalizedStringKey
in the localized
computed property. We pass this localized
property to the Text
constructor so that it generates the correct localized string.Reason Two
Another reason why this issue may occur is when using a NavigationView
in the view. You can select the navigation view, but not any subview. To solve this, you can move all the subviews of the navigation view into a separate view.
For example, the following code would cause the issue:
import SwiftUI
struct TestView: View {
var body: some View {
NavigationView {
VStack {
Text("One")
Text("Two")
Text("Three")
}
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
The only way to solve this issue so far is to move all the subviews of the navigation view into a separate view.