Reputation: 8091
Problem: i tried to make this example https://swiftui-lab.com/communicating-with-the-view-tree-part-1/ a bit swiftier by not using the same MonthView() 12 times but in a loop. Unfortunately when tapping the label/month the variable activeIdx won't be updated and I have no idea why... and my question is: how do i have to change the code that the binding works? Expected behaviour: When you tap on the month names the red border should mark the label you tapped.
import SwiftUI
let months : [String] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
struct ContentView : View {
@State private var activeIdx: Int = 0
var groupedGroupedViews : [[MonthView]] = []
init() {
var groupedViews : [MonthView] = []
for idx in 0..<months.count {
print(idx)
groupedViews.append(MonthView(activeMonth: self.$activeIdx, label: months[idx], idx: idx))
print(months[idx])
if (idx + 1) % 4 == 0 {
groupedGroupedViews.append(groupedViews)
groupedViews = []
}
}
groupedGroupedViews.append(groupedViews)
}
var body: some View {
VStack {
ForEach (groupedGroupedViews, id: \.self) { groupedViews in
HStack {
ForEach (groupedViews, id: \.idx) { view in
view
}
}
}
}
}
}
struct MonthView: View, Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(idx)
}
static func == (lhs: MonthView, rhs: MonthView) -> Bool {
return lhs.idx == rhs.idx
}
@Binding var activeMonth: Int
let label: String
var idx: Int
var body: some View {
Text(label)
.padding(10)
.onTapGesture {
print(self.idx, " tapped")
self.activeMonth = self.idx
print("active = ", self.activeMonth)
}
.background(MonthBorder(show: activeMonth == idx))
}
}
struct MonthBorder: View {
let show: Bool
var body: some View {
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 3.0).foregroundColor(show ? Color.red : Color.clear)
.animation(.easeInOut(duration: 0.6))
}
}
Upvotes: 0
Views: 55
Reputation: 11531
The reason is you cannot init
a binding View like that.
var myView : MonthView!
init() {
myView = MonthView(activeMonth: self.$activeIdx, label: months[0], idx: 1)
}
var body: some View {
myView}
If you run above code, you still can see activeMonth
is not changing.
I think the init() mainly bridges some UIKit features and should be not used as a major way in SwiftUI. Cross platform is necessary but try to avoid if possible. Please appreciate the design merit of swiftUI not the overhead from traditional implementations.
One simple answer to this is moving the init
to View. That reaches the same function you need.
var body: some View {
let groupedGroupedViews : [[MonthView]] = {
var groupedGroupedViews : [[MonthView]] = []
var groupedViews : [MonthView] = []
for idx in 0..<months.count {
print(idx)
groupedViews.append( MonthView(activeMonth: self.$activeIdx, label: months[idx], idx: idx))
print(months[idx])
if (idx + 1) % 4 == 0 {
groupedGroupedViews += [groupedViews]
groupedViews.removeAll()
}
}
groupedGroupedViews += [groupedViews]
return groupedGroupedViews
}()
return VStack {
ForEach (groupedGroupedViews, id: \.self) { groupedView in
HStack {
ForEach (groupedView, id: \.idx) { view in
view
}
}
}
}
}
Upvotes: 1
Reputation: 8091
I found it, thanks to E.Coms, i have changed my code and now it works. Here my solution (but i am pretty sure you could do better)
struct ViewValue : Hashable {
var idx: Int
var text: String
}
struct ContentView : View {
@State private var activeIdx: Int = 0
var groupedGroupedValues : [[ViewValue]] = []
init() {
var groupedValues : [ViewValue] = []
for idx in 0..<months.count {
print(idx)
groupedValues.append(ViewValue(idx: idx, text: months[idx]))
print(months[idx])
if (idx + 1) % 4 == 0 {
groupedGroupedValues.append(groupedValues)
groupedValues = []
}
}
groupedGroupedValues.append(groupedValues)
}
var body: some View {
VStack {
ForEach (groupedGroupedValues, id: \.self) { groupedValue in
HStack {
ForEach (groupedValue, id: \.idx) { viewValue in
MonthView(activeMonth: self.$activeIdx, label: viewValue.text, idx: viewValue.idx)
}
}
}
}
}
}
Upvotes: 0