SwiftUI视图未更新,但模型已正确更新

  • 本文关键字:更新 模型 视图 SwiftUI swiftui
  • 更新时间 :
  • 英文 :


我尝试构建一个计数器。这是我的模型:

var encoder = JSONEncoder()
var decoder = JSONDecoder()
class CounterItems: ObservableObject {
@Published var counterItems: [Counter]
var index = 0
init() {
counterItems = []
}
init(data: [Counter]) {
counterItems = []
for item in data {
counterItems.append(Counter(id: index, name: item.name, count: item.count, step: item.step, isDeleted: item.isDeleted))
index += 1
}
}
func AddNewCounter(newCounter: Counter) {
counterItems.append(Counter(id: index, name: newCounter.name, count: newCounter.count, step: newCounter.step, isDeleted: newCounter.isDeleted))
index += 1
storeData()
}
func minus(index: Int) {
if counterItems[index].count >= counterItems[index].step {
counterItems[index].count -= counterItems[index].step
}
storeData()
}
func plus(index: Int) {
counterItems[index].count += counterItems[index].step
storeData()
}
func edit(index: Int, data: Counter) {
counterItems[index].name = data.name
counterItems[index].step = data.step

storeData()
}

func reset(index: Int) {
counterItems[index].count = 0
storeData()
}
func resetAll() {
for item in counterItems {
reset(index: item.id)
}
storeData()
}
func delete(index: Int) {
counterItems[index].isDeleted = true
storeData()
}
func deleteAll() {
for item in counterItems {
delete(index: item.id)
}
storeData()
}
func storeData() {
let dataToStore = try! encoder.encode(counterItems)
UserDefaults.standard.set(dataToStore, forKey: "counterItems")
}
}

这是我的观点:

struct ContentView: View {

@ObservedObject var userData: CounterItems = CounterItems(data: initUserData())
@State var isShowingAddCounterView = false
@State var isShowingResetingDialog = false
@State var isShowingDeletingDialog = false

var body: some View {
NavigationView {
VStack {
ScrollView {
VStack {
ForEach(userData.counterItems) { item in
if item.isDeleted == false {
SingleCounterView(index: item.id)
.environmentObject(userData)
}
}
}
}
.navigationTitle("Tally Counter")

HStack(spacing: 130) {
Button(action: {
isShowingResetingDialog = true
}, label: {
Image(systemName: "gobackward")
.imageScale(.large)
.foregroundColor(.accentColor)
})
.alert(isPresented: $isShowingResetingDialog) {
Alert(title: Text("Will reset all counters"),
primaryButton: .default(
Text("Confirm"),
action: {userData.resetAll()}
),
secondaryButton: .cancel(Text("Cancel"))
)
}

Button(action: {
isShowingAddCounterView = true
}, label: {
Image(systemName: "plus.circle.fill")
.imageScale(.large)
.foregroundColor(.accentColor)
})
.sheet(isPresented: $isShowingAddCounterView, content: {
AddCounterView().environmentObject(userData)
})

Button(action: {
isShowingDeletingDialog = true
}, label: {
Image(systemName: "trash")
.imageScale(.large)
.foregroundColor(.accentColor)
})
.alert(isPresented: $isShowingDeletingDialog) {
Alert(title: Text("Will delete all counters!"),
primaryButton: .default(
Text("Confirm"),
action: {userData.deleteAll()}
),
secondaryButton: .cancel(Text("Cancel"))
)
}
}
}
}
}
}
struct SingleCounterView: View {
@EnvironmentObject var userData: CounterItems
var index: Int
@State var isShowingEditingView = false

var body: some View {
ZStack {
Rectangle()
.foregroundColor(Color("Color(index%5 + 1)"))
.frame(height: 150)
.cornerRadius(20)
.padding([.trailing, .leading])
.shadow(radius: 5, x: 0, y: 5)
HStack(spacing: 20) {

Button(action: {
userData.minus(index: index)
HapticManager.instance.impact(style: .medium)
}, label: {
Image(systemName: "minus.circle")
.resizable()
.frame(width: 40, height: 40)
.foregroundColor(.white)
.padding()
})

VStack(spacing: 10) {

Button(action: {
isShowingEditingView = true
}, label: {
VStack(spacing: 10) {
Text(userData.counterItems[index].name)
Text("(userData.counterItems.first(where: {$0.id == index})!.count)")
.font(.system(size: 60))
.frame(width: 100)
}

})
.sheet(isPresented: $isShowingEditingView, content: {
AddCounterView(userData: _userData, name: userData.counterItems[index].name, step: userData.counterItems[index].step, index: index)
})

NavigationLink(destination: {
SingleCounterFullView(index: index).environmentObject(userData)
}, label: {
Image("quanping")
.resizable()
.frame(width: 20, height: 20)
})

}
.foregroundColor(.white)
.padding()



Button(action: {
userData.plus(index: index)
HapticManager.instance.impact(style: .medium)
}, label: {
Image(systemName: "plus.circle")
.resizable()
.frame(width: 40, height: 40)
.foregroundColor(.white)
.padding()
})
}


}
}
}
func initUserData() -> [Counter] {
var output: [Counter] = []
if let storedData = UserDefaults.standard.object(forKey: "counterItems") as? Data {
let data = try! decoder.decode([Counter].self, from: storedData)
for item in data {
if !item.isDeleted {
output.append(Counter(id: output.count, name: item.name, count: item.count, step: item.step, isDeleted: item.isDeleted))
}
}
}
return output
}

所有这些功能在实时预览和模拟器中都能很好地工作。但是当在我的iPhone 12上运行时,SingleCounterView中单个计数器的count有时不会更新,直到我按下另一个SingleCounterView的加减按钮。然而,userData.counterItems[index].count仍然更新,工作良好。

。我有一个名为"Books"的计数器,当前的count是3。当加号按钮被按下时,SingleCounterView中的count不会更新为4,直到我在另一个计数器中按下另一个加号或减号按钮。但是SingleCounterFullView中的count总是正确的,并且在按加号或减号后更新。

注意:该问题可能发生在任何计数器上,但一次只能发生在一个计数器上。一旦计数器作为问题,它就继续与之相关。这并不总是会发生。似乎使用绑定可以抑制这个问题:

ForEach(userData.counterItems) { item in
let index = item.id
if item.isDeleted == false {
// pass the array item instead of index in array
SingleCounterView(counter: $userData.counterItems[index])
.environmentObject(userData)
}
}

更新视图以使用到数组元素

的绑定
struct SingleCounterBindingView: View {

@EnvironmentObject var userData: CounterItems
@Binding var counter: Counter
var index: Int {
counter.id
}
@State var isShowingEditingView = false


var body: some View {
HStack(spacing: 20) {

Button(action: {
counter.minus()
}, label: {
Image(systemName: "minus.circle")
.resizable()
.frame(width: 40, height: 40)
.padding()
})

VStack(spacing: 10) {
Button(action: {
isShowingEditingView = true
}, label: {
VStack(spacing: 10) {
Text(counter.name)
Text("(counter.count)")
.font(.system(size: 60))
.frame(width: 100)
}

})
.sheet(isPresented: $isShowingEditingView, content: {
AddCounterView(userData: _userData, name: counter.name, step: counter.step, index: index)
})

NavigationLink(destination: {
SingleCounterFullView(index: index).environmentObject(userData)
}, label: {
Image(systemName: "info")
.resizable()
.frame(width: 20, height: 20)
})

}
.padding()

Button(action: {
counter.plus()
}, label: {
Image(systemName: "plus.circle")
.resizable()
.frame(width: 40, height: 40)
.padding()
})
}
}
}

创建一个计数器结构来处理修改:

struct Counter: Codable, Identifiable {
var id: Int
var name: String
var count: Int
var step: Int
var isDeleted: Bool

mutating func plus() {
count += step
}

mutating func minus() {
if count >= step {
count -= step
}
}

mutating func reset() {
count = 0
}

mutating func delete() {
isDeleted = true
}

mutating func edit(from counter: Counter) {
name = counter.name
step = counter.step
}
}

//更新模型使用Counter的新函数

class CounterItems: ObservableObject {
@Published var counterItems: [Counter]

var index = 0

init() {
counterItems = []
}

init(data: [Counter]) {
counterItems = []
for item in data {
counterItems.append(Counter(id: index, name: item.name, count: item.count, step: item.step, isDeleted: item.isDeleted))
index += 1
}
}

func addNewCounter(newCounter: Counter) {
counterItems.append(Counter(id: index, name: newCounter.name, count: newCounter.count, step: newCounter.step, isDeleted: newCounter.isDeleted))
index += 1
storeData()
}

func minus(index: Int) {
counterItems[index].minus()
storeData()
}

func plus(index: Int) {
counterItems[index].plus()
storeData()
}

func edit(index: Int, data: Counter) {
counterItems[index].edit(from: data)
storeData()
}

func reset(index: Int) {
counterItems[index].reset()
storeData()
}

func resetAll() {
for item in counterItems {
reset(index: item.id)
}
// note storeData is not needed
}

func delete(index: Int) {
counterItems[index].delete()
storeData()
}

func deleteAll() {
for item in counterItems {
delete(index: item.id)
}
// note storeData is not needed
}

func storeData() {
let dataToStore = try! encoder.encode(counterItems)
UserDefaults.standard.set(dataToStore, forKey: "counterItems")
}
}

注意:I抑制了所有颜色和触觉以简化示例代码注2:我假设索引和项目。它在你的例子中似乎是等效的。但是index可以是使用firstIndex计算的变量。

最新更新