I have a list of SwiftUI Text instances, and I want to be able to alter their characteristics (in this example, font weight) after initial creation of the body. It looks like SwiftUI doesn't want you to perform modifying functions on View elements after placing them in the body (i.e. Text().fontWeight() doesn't change the weight of the text in the instance Text() gave you, but returns a different instance which has the font weight you want, which means that these modifying methods only make sense when applied within your view's body, I guess).
My attempts to work with this paradigm have failed, however. Consider the following code to display a line of digits and a "Click Me" button which is intended to bold a different digit with every click. It initializes an array of boldable Text instances. In order to make ForEach work, because Text doesn't conform to Hashable or Identifiable, I needed to make a struct BoldableText which has an identifier, a Text instance, and the intended weight of that Text instance.
A function, highlight(), is intended to advance the bolded text to the next digit, wrapping around to 0 when it reaches the end.
struct BoldableText: Identifiable {
var id: Int // So that it works with ForEach in the View
var theWeight: Font.Weight // The weight of this Text item
var theText:Text // The actual Text item
}
let DIGITS = 6 // How many digits to display
var idx_of_bolded:Int = 0 // Which of them is currently bold?
// A view to show an array of Text items, each boldable after user interaction
struct ArrayView: View {
@State var numbers:[BoldableText] = []
// Set up the numbers array with digit texts with regular font weight
init() {
numbers = []
for i in 0..<DIGITS {
numbers.append(BoldableText(id: i, theWeight:Font.Weight.regular, theText: Text(String(i))))
}
}
// Advance the highlighted number and highlight that text
func highlight() {
numbers[idx_of_bolded].theWeight = Font.Weight.regular
idx_of_bolded = (idx_of_bolded + 1) % DIGITS
numbers[idx_of_bolded].theWeight = Font.Weight.heavy
}
var body: some View {
HStack {
// Display the numbers in a line
ForEach(numbers) { number in
Text(String(number.id)).fontWeight(number.theWeight)
}
Button(action: highlight) {
Text("Click to advance the number")
}
}
}
}
The problem is, this won't even build. The two lines in highlight() which try to change the .theWeight property get flagged for trying to mutate numbers[].
If I fix this by marking func highlight() as mutating, Xcode flags the call to it from the Button() action.
Alternately, I can fix it by moving var numbers ... outside of the struct, but then I can't use @State on it.
That gets it to build, but clicking the button doesn't do anything, I'm guessing because nothing is bound to the font weights, Swift thinks they're just static values.
Meanwhile, if I try binding by using fontWeight($number.theWeight), Xcode complains that it "can't find $number in scope". Same goes for ForEach($numbers)
If I bring @State var numbers... back into the struct and keep ForEach($numbers) the error at fontWeight($number.theWeight) changes to a very intriguing "Cannot convert value of type 'Binding<Font.Weight>' to expected argument type 'Font.Weight?'" (which suggests to me that fontWeight doesn't even accept bound parameters, which seems odd).
Is it possible to even do this with SwiftUI, or must I go back to using a ViewController?
