我正在做一个页面,其中包含5个带单选按钮的选项。当我们点击一个选项时,元素的颜色应该改变。我正在从API响应中获取这些选项。我正在使用MVVM模型。我附上下面的代码。
我正在努力的是当我更改一个布尔值时,它没有改变!
查看模型代码
import Foundation
import Combine
class DCListViewModel: ObservableObject {
@Published var DCList = [DCViewModel]()
init() {
fetchDcs()
}
func fetchDcs() {
ARMServices().getAllDc { (dcArr) in
if let dcArr = dcArr {
for dc in dcArr{
self.DCList.append(DCViewModel(dc: dc, isSelected: false))
}
}
}
}
}
class DCViewModel {
var id = UUID()
var dcStruct: DC
var isSelected: Bool
init(dc: DC, isSelected: Bool) {
self.dcStruct = dc
self.isSelected = isSelected
}
var url: String {
return self.dcStruct.url ?? "empty URL"
}
var dc: String {
return self.dcStruct.dc ?? "empty dc name"
}
}
查看页面代码
struct SelectTableView: View {
var body: some View {
NavigationView {
VStack {
DCListView(dcList: self.dcListVM.DCList)
.padding(.horizontal)
}
.navigationBarTitle("Select the Data Centre")
}
}
}
struct DCListView: View{
var dcList: [DCViewModel]
init(dcList: [DCViewModel]) {
self.dcList = dcList
}
var body: some View {
ForEach(self.dcList, id: \.id) { dc in
Button(action: {
print("Tapped")
dc.isSelected.toggle()
}){
ZStack {
RoundedRectangle(cornerRadius: 8)
.stroke(dc.isSelected ? Color.init("borderSelected"): Color.init("border"))
.frame(height: 56)
.foregroundColor(.clear)
HStack {
Text(dc.dc)
.font(.custom("Montserrat", size: 16))
.fontWeight(.medium)
.foregroundColor(dc.isSelected ? Color.init("borderSelected") : .white)
.padding()
Spacer()
ZStack {
Circle()
.stroke(dc.isSelected ? Color.init("borderSelected") : Color("circleBorder"))
.frame(width: 18, height: 18)
.padding()
Circle()
.frame(width: 10, height: 10)
.foregroundColor(dc.isSelected ? Color.init("borderSelected"): Color.clear)
}
}
}
}
}
}
}
答案 0 :(得分:2)
将DCViewModel
更改为struct
。
或者如果您必须将其保留为class
,则使DCViewModel
符合ObservableObject
,并在每个View
中保留一个引用。 (这意味着您应将该行减去为单个View
)
@Published是一个属性包装程序,仅在设置了包装的值 时,才发布更改消息。
值类型和引用类型之间的区别:当您更改一个持有值类型(结构)的变量时,实际上已为其设置了一个新值。但是,当您更改引用类型(类)的值时,指向该对象的变量将不会更改。
这就是为什么在您的情况下,当向DCViewModel
(值类型)添加新的Array
时,总是为该数组设置一个新值,所以@Published
采用影响。但是,包装的值为DCViewModel
,它是一个类(引用类型)。更改DCViewModel
的值时,将不会调用包装的值的setter,并且@Published
不会发布消息。
答案 1 :(得分:1)
有两个选项。
DCViewModel
class
更改为struct
。DCListViewModel
class
进行发布。 DCViewModel
class
更改为struct
。struct DCViewModel {
var id: UUID
var dcStruct: DC
var isSelected: Bool
init(dc: DC, isSelected: Bool) {
self.id = UUID()
self.dcStruct = dc
self.isSelected = isSelected
}
var url: String {
return self.dcStruct.url ?? "empty URL"
}
var dc: String {
return self.dcStruct.dc ?? "empty dc name"
}
}
struct SelectTableView: View {
// dcListVM missed
// I assume superview of SelectTableView exist.
@ObservedObject var dcListVM: DCListViewModel
var body: some View {
NavigationView {
VStack {
DCListView(dcList: $dcListVM.DCList)
.padding(.horizontal)
}
.navigationBarTitle("Select the Data Centre")
}
}
}
struct DCListView: View{
@Binding var dcList: [DCViewModel] // Use @Binding for two-way binding
var body: some View {
// You can't use `ForEach(data: self.dcList, id: \.id ) { dc in`
// Because `dc` is immutable
ForEach(0..<self.dcList.count) { dc in
Button(action: {
print("Tapped")
self.dcList[dc].isSelected.toggle()
}){
ZStack {
RoundedRectangle(cornerRadius: 8)
.stroke(self.dcList[dc].isSelected ? Color.red: Color.black)
.frame(height: 56)
.foregroundColor(.clear)
HStack {
Text(self.dcList[dc].dc)
.font(.system(size: 16))
.fontWeight(.medium)
.foregroundColor(self.dcList[dc].isSelected ? Color.red : Color.black)
.padding()
Spacer()
ZStack {
Circle()
.stroke(self.dcList[dc].isSelected ? Color.red : Color.black)
.frame(width: 18, height: 18)
.padding()
Circle()
.frame(width: 10, height: 10)
.foregroundColor(self.dcList[dc].isSelected ? Color.red: Color.clear)
}
}
}
}
}
}
}
DCListViewModel
实例。struct SelectTableView: View {
// dcListVM missed
// I assume superview of SelectTableView exist.
var dcListVM: DCListViewModel()
var body: some View {
NavigationView {
VStack {
DCListView(dcList: self.dcListVM) // DCListViewModel instead of [DCViewModel]
.padding(.horizontal)
}
.navigationBarTitle("Select the Data Centre")
}
}
}
struct DCListView: View {
@ObservedObject var dcListVM: DCViewModel // DCListViewModel instead of [DCViewModel]
// See "Memberwise Initializers for Structure Types" in docs.swift.org
// init(dcListVM: DCViewModel) {
// self.dcList = dcList
// }
var body: some View {
ForEach(self.dcList.DCList, id: \.id) { dc in
Button(action: {
print("Tapped")
dc.isSelected.toggle()
self.dcList.objectWillChange.send() // Manually make dcList to publish
}){
ZStack {
RoundedRectangle(cornerRadius: 8)
.stroke(dc.isSelected ? Color.init("borderSelected"): Color.init("border"))
.frame(height: 56)
.foregroundColor(.clear)
HStack {
Text(dc.dc)
.font(.custom("Montserrat", size: 16))
.fontWeight(.medium)
.foregroundColor(dc.isSelected ? Color.init("borderSelected") : .white)
.padding()
Spacer()
ZStack {
Circle()
.stroke(dc.isSelected ? Color.init("borderSelected") : Color("circleBorder"))
.frame(width: 18, height: 18)
.padding()
Circle()
.frame(width: 10, height: 10)
.foregroundColor(dc.isSelected ? Color.init("borderSelected"): Color.clear)
}
}
}
}
}
}
}
答案 2 :(得分:0)
您只是将dclist副本“提供”给视图,因此视图不知道会发生什么更改。将@binding添加到变量dclist的视图中。