我从 PHPicker 中挑选照片并在SwiftUI 中的VStack
usingForEach
循环中显示它们。我将图像包装在具有 3 个属性的对象中。
class UploadedImage: Hashable, Equatable {
var id: Int
var image : UIImage
@State var quantity: Int
init(id: Int, image: UIImage, quantity: Int) {
self.id = id
self.image = image
self.quantity = quantity
}
static func == (lhs: UploadedImage, rhs: UploadedImage) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在选择器选择图像后,我将该对象添加到数组中,然后使用 ForEach 在屏幕上显示。
if !uploadedImages.isEmpty {
ScrollView {
VStack{
ForEach(uploadedImages, id: \.self) { selectedImage in
HStack{
ZStack{
// some other code to display image...
}
Spacer()
HStack{
Button(action: {
if let index = self.uploadedImages.firstIndex(where: {$0.id == selectedImage.id}){
uploadedImages[index] = UploadedImage(id: selectedImage.id, url: selectedImage.url, quantity: (1))
}
}, label: {
Text("-")
})
Text("\(selectedImage.quantity)")
Button(action: {
if let index = self.uploadedImages.firstIndex(where: {$0.id == selectedImage.id}){
uploadedImages[index] = UploadedImage(id: selectedImage.id, url: selectedImage.url, quantity: selectedImage.quantity + 1)
}
}, label: {
Text("+")
})
}
}
.padding()
.border(Color.black, width: 2)
.cornerRadius(6)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
在 ForEach 循环中,我显示了两个按钮,+, -
用于增加和减少数量。
该按钮上的操作是以下代码。
if let index = self.uploadedImages.firstIndex(where: {$0.id == selectedImage.id}){
uploadedImages[index] = UploadedImage(id: selectedImage.id, url: selectedImage.url, quantity: selectedImage.quantity + 1)
}
Run Code Online (Sandbox Code Playgroud)
但数量没有更新。我不知道为什么。
id
当我在新更新的对象中更改时。
uploadedImages[index] = UploadedImage(id: 89282, url: selectedImage.url, quantity: selectedImage.quantity + 1)
Run Code Online (Sandbox Code Playgroud)
那么数量只更新一次。
有什么好的办法增加和减少数量吗?
SwiftUI 中的数据流最初可能有点难以适应,具体取决于您\xe2\x80\x99 来自哪个框架。
\n有多种方法可以从高层角度来处理它,但考虑到您的问题是关于单个视图、其模型和按钮的本地化问题,我将重点关注来自单个视图\xe2\x80\x99s 的数据看法。
\n然后,为了了解数据如何在视图之间流动、它如何与 SwiftUI\xe2\x80\x99s 视图生命周期交互等等,我会推荐以下两个 WWDC 会议:
\n\xe2\x80\x99s 还有 Apple 提供的这份文档,其中包含相当详细的分类:\n模型数据。\nSwiftUI 教程中的这一部分:管理状态和生命周期
\n返回到 View\xe2\x80\x99s 视角。\n根据数据的两个特征,在视图中设置数据的方式将有很大不同:
\n因此,在该矩阵的简单端,您有一个让 SwiftUI 为您处理的值类型。\n这就是@State的用途。
\n在视图内部,您可以像这样使用 @State 来处理字符串:
\n@State var name = \xe2\x80\x9cBob\xe2\x80\x9d\n
Run Code Online (Sandbox Code Playgroud)\n但结构体也可以工作:
\n@State var selectedUser = MyUserStruct()\n
Run Code Online (Sandbox Code Playgroud)\n关于 @State 需要了解的重要一点是,它明确意味着可能的场景的简单结束。
\n@状态
\n它\xe2\x80\x99s 用于表示视图内部状态的数据。
\n请注意,在这种情况下,无需对我们的数据执行任何操作,即可让 SwiftUI 了解何时更新视图。(至少对于大多数用例而言。)
\n复杂性上升:\n如果您希望 SwiftUI 处理数据,但正在处理引用类型,该怎么办?\n这是您当前的设置。
\n与值类型示例相比,SwiftUI 需要额外的帮助来了解何时更新依赖于引用类型的视图。
\n因此,任何应驱动视图更新的类都需要符合ObservableObject协议。\n此外,在这些类中,只有标记为@Published 的属性在这些属性更改时才会真正驱动视图更新。
\n然后,要在视图中使用 @ObservableObject,您可以选择几个具有截然不同行为的属性包装器:\n @StateObject、@ObservedObject和@EnvironmentObject。
\n@StateObject 的行为与@State 最接近。该对象由 SwiftUI 管理,并且初始化为 @StateObject 的 ObservableObject 的生命周期绑定到初始化它的 View 的生命周期。\n@ObservedObject 和 @EnvironmentObject 与我们当前的不匹配复杂性矩阵中的位置,因为它们适用于您不希望 SwiftUI 处理您的数据生命周期(或不直接处理)的情况。
\n但这实际上为我们提供了足够的信息来重组您的示例并保持在我们的单一视图视角内。
\n让\xe2\x80\x99s 首先从字面上看你的例子,并假装我们只想在数组中进行这些本地化更新,而\xe2\x80\x99s 就是它。\n对于这种情况,@State 实际上可以工作,但我们需要将 UploadedImage 模型更改为结构:
\nstruct UploadedImage: Identifiable {\n let id: Int\n let image: Image\n var quantity: Int\n}\n
Run Code Online (Sandbox Code Playgroud)\n两个注意事项:
\n现在我们\xe2\x80\x99将在视图中通过@State使用它:
\n@State var uploadedImages = [\n UploadedImage(id: 1, image: someImage, quantity: 4),\n UploadedImage(id: 2, image: someImage, quantity: 8),\n UploadedImage(id: 3, image: someImage, quantity: 16)\n]\n
Run Code Online (Sandbox Code Playgroud)\n如上所述,ForEach 可以简化为:
\nForEach(uploadedImages) { selectedImage in\n // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n而 \xe2\x80\x99 实际上是使您的示例正常工作所需的全部内容。
\n但正如其他人指出的那样,\xe2\x80\x99s 可能你可能有其他逻辑需要处理,并且将一些非 UI 逻辑拆分到单独的视图模型中可能会很好,让视图进行交互使用该模型,并观察它的变化。
\n通常,视图模型采用类的形式,因此我们改变上面讨论的引用类型路线的方法:我们的视图模型需要符合 ObservableObject,使用 @Published 来显示任何应该驱动更新的数据,并且它得到通过@StateObject交给SwiftUI来管理。
\n综合起来,该解决方案如下所示:
\n@MainActor class UploadedImagesViewModel: ObservableObject {\n @Published var images = [\n UploadedImage(id: 3, image: someImage, quantity: 4),\n UploadedImage(id: 4, image: someImage, quantity: 8),\n UploadedImage(id: 5, image: someImage, quantity: 16)\n ]\n\n func increment(_ selectedImage: UploadedImage) {\n if let index = self.images.firstIndex(where: {$0.id == selectedImage.id}){\n images[index].quantity += 1\n }\n }\n\n func decrement(_ selectedImage: UploadedImage) {\n if let index = self.images.firstIndex(where: {$0.id == selectedImage.id}){\n images[index].quantity -= 1\n }\n }\n}\n\nstruct UploadedImage: Identifiable {\n let id: Int\n let image: Image\n var quantity: Int\n}\n\nstruct UploadedImagesView: View {\n\n @StateObject var viewModel = UploadedImagesViewModel()\n\n var body: some View {\n \n if !viewModel.images.isEmpty {\n ScrollView {\n VStack{\n ForEach(viewModel.images) { selectedImage in\n HStack{\n ZStack{\n // some other code to display image...\n }\n Spacer()\n HStack{\n Button(action: {\n viewModel.decrement(selectedImage)\n }, label: {\n Text("-")\n })\n Text("\\(selectedImage.quantity)")\n Button(action: {\n viewModel.increment(selectedImage)\n }, label: {\n Text("+")\n })\n }\n }\n .padding()\n .border(Color.black, width: 2)\n .cornerRadius(6)\n }\n }\n }\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n最后一些最后的注意事项:
\n 归档时间: |
|
查看次数: |
1821 次 |
最近记录: |