我有两个 Swift UI 视图,
CategoriesListView
和 CategoryView.
CategoriesListView
的工作方式有点像导航菜单,CategoryView
是一个可以点击的视图。我正在尝试以下操作:
CategoryView
),该类别将显示突出显示的图像不幸的是,一旦点击某些内容,类别视图就不会改变。我必须在视图模型中做一些事情吗?我对 SwiftUI 还很陌生,并且仍在学习很多东西
我的看法:
struct CategoriesListView: View {
@Environment(CategoriesListViewModel.self) var categoriesListViewModel
var selectedIndex: Int { categoriesListViewModel.categories.firstIndex(where: {
$0.isSelected == true
}) ?? 0
}
var body: some View {
@Bindable var categoriesListViewModel = categoriesListViewModel
ScrollView(.horizontal) {
HStack(alignment: .top, spacing: 12) {
Spacer()
// With each category in the categories list view model, create a category view. If the category view gets tapped, change that view to be the selected image. the previously selected view shows an unselected image.
ForEach(Array(categoriesListViewModel.categories.enumerated()), id: \.offset) { index, element in
CategoryView(categoryViewModel: $categoriesListViewModel.categories[index]).onTapGesture {
categoriesListViewModel.categories[selectedIndex].isSelected = false
categoriesListViewModel.categories[index].isSelected = true
CategoryView(categoryViewModel: $categoriesListViewModel.categories[index])
}
}
}
}
}
}
struct CategoryView: View {
@Binding var categoryViewModel: CategoryViewModel
var body: some View {
if categoryViewModel.isSelected {
categoryViewModel.category.highlightedIconImage
} else {
categoryViewModel.category.iconImage
}
}
}
我的视图模型:
@Observable
class CategoriesListViewModel {
var categories: [CategoryViewModel] = []
var currentSelection: Int = 0
var previousSelection: Int? = nil
init(categories: [CategoryViewModel], currentSelection: Int, previousSelection: Int? = nil) {
self.categories = setCategoriesToShow()
self.currentSelection = currentSelection
self.previousSelection = previousSelection
}
func setCategoriesToShow() -> [CategoryViewModel] {
var categoriesToShow = [CategoryViewModel]()
let resortCategory = Category(
identifier: "a",
title: "Resort",
outfits: [],
iconImage: Image("ResortUnselected"),
highlightedIconImage: Image("ResortSelected")
)
var europeCategory = Category(
identifier: "b",
title: "Europe",
outfits: [],
iconImage: Image("EuropeUnselected"),
highlightedIconImage: Image("EuropeSelected")
)
var brunchCategory = Category(
identifier: "c",
title: "Brunch",
outfits: [],
iconImage: Image("BrunchUnselected"),
highlightedIconImage: Image("BrunchSelected")
)
var athleisureCategory = Category(
identifier: "d",
title: "Athleisure",
outfits: [],
iconImage: Image("AthleisureUnselected"),
highlightedIconImage: Image("AthleisureSelected")
)
var workCategory = Category(
identifier: "e",
title: "Work",
outfits: [],
iconImage: Image("WorkUnselected"),
highlightedIconImage: Image("WorkSelected")
)
categoriesToShow.append(CategoryViewModel(category: resortCategory, isSelected: true))
categoriesToShow.append(CategoryViewModel(category: europeCategory, isSelected: false))
categoriesToShow.append(CategoryViewModel(category: brunchCategory, isSelected: false))
categoriesToShow.append(CategoryViewModel(category: athleisureCategory, isSelected: false))
categoriesToShow.append(CategoryViewModel(category: workCategory, isSelected: false))
return categoriesToShow
}
}
class CategoryViewModel: ObservableObject, Identifiable {
var category: Category
var isSelected: Bool
init(category: Category, isSelected: Bool) {
self.category = category
self.isSelected = isSelected
}
}
请勿混合
ObservableObject
和 @Observable
(例如您的 CategoryViewModel)。
另外,当您使用 Identifiable
时,您需要有 let id...
。
尝试这种方法清理
ForEach
中的 CategoriesListView
并删除 @Bindable var categoriesListViewModel = categoriesListViewModel
ForEach(Array(categoriesListViewModel.categories.enumerated()), id: \.offset) { index, element in
CategoryView(categoryViewModel: categoriesListViewModel.categories[index])
.onTapGesture {
categoriesListViewModel.categories[selectedIndex].isSelected = false
categoriesListViewModel.categories[index].isSelected = true
}
}
或者不使用索引,更好,推荐
ForEach(categoriesListViewModel.categories) { category in
CategoryView(categoryViewModel: category)
.onTapGesture {
// turn off all selections
categoriesListViewModel.categories.forEach{
$0.isSelected = false
}
// turn on only this one
category.isSelected = true
}
}
和
@Observable
class CategoryViewModel: Identifiable {
let id = UUID()
var category: Category
var isSelected: Bool
init(category: Category, isSelected: Bool) {
self.category = category
self.isSelected = isSelected
}
}
struct CategoryView: View {
var categoryViewModel: CategoryViewModel
var body: some View {
if categoryViewModel.isSelected {
categoryViewModel.category.highlightedIconImage
} else {
categoryViewModel.category.iconImage
}
}
}
这是假设您已声明 视图层次结构中的
@State private var model = CategoriesListViewModel(....)
并使用 .environment(model)
传递此模型
在 SwiftUI 中,视图结构已经是视图模型(你不需要自己的对象),像选择这样的动态视图数据应该是与模型分离的
@State
,例如
@State var selectedCategory: Category?
var body: some View {
List(selection: $selectedCategory) {
ForEach(model.categories) {
...
选择更改时会调用 body
。