I have a following View (took out irrelevant parts):
struct Chart : View {
var xValues: [String]
var yValues: [Double]
@State private var showXValues: Bool = false
var body = some View {
...
if showXValues {
...
} else {
...
}
...
}
}
then I wanted to add a way to modify this value from outside, so I added a function:
func showXValues(show: Bool) -> Chart {
self.showXValues = show
return self
}
so I build the Chart view from the outside like this:
Chart(xValues: ["a", "b", "c"], yValues: [1, 2, 3])
.showXValues(true)
but it works as if the value was still false. What am I doing wrong? I thought updating an @State variable should update the view. I am pretty new to Swift in general, more so to SwiftUI, am I missing some kind of special technique that should be used here?
There is no need to create func
-s. All I have to do is not mark the properties as private but give them an initial value, so they're gonna become optional in the constructor. So user can either specify them, or not care. Like this:
var showXLabels: Bool = false
This way the constructor is either Chart(xLabels:yLabels)
or Chart(xLabels:yLabels:showXLabels)
.
Question had nothing to do with @State.
Edit, a few years later
Actually, there is an even cleaner way to solve this for binary options, like show/hide stuff. For more than 2-way configurations, distinct arguments are still the way.
The point is that OptionSet
s are intended for exactly this use case, and they can be found throughout Apple frameworks like Foundation etc.
So, we are just gonna add a Chart.Options
struct:
extension Chart {
struct Options: OptionSet {
let rawValue: Int
static var showXValueLabels = Self(rawValue: 1 << 0)
static var showYValueLabels = Self(rawValue: 1 << 1)
[... add more options if you want]
}
}
Then, the View itself remains more compact, because it will only have a single variable for binary settings:
struct Chart: View {
var xValues: [String]
var yValues: [Int]
var options: Chart.Options = []
var body: some View {
VStack {
Text("A chart")
if options.contains(.showXValueLabels) {
[... whatever xValue label specific view]
}
if options.contains(.showYValueLabels) {
[... whatever yValue label specific view]
}
}
}
}
And you can construct a Chart
like this:
Chart(xValues: ["a", "b", "c"],
yValues: [1, 2, 3],
options: [.showXValueLabels, .showYValueLabels])
or
Chart(xValues: ["a", "b", "c"],
yValues: [1, 2, 3],
options: [.showXValueLabels])
or just use the default options:
Chart(xValues: ["a", "b", "c"],
yValues: [1, 2, 3])