为什么我的视图没有使用 MVVM 架构更新?

问题描述 投票:0回答:2

我有两个 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
    }
}
swift xcode swiftui mvvm
2个回答
0
投票

请勿混合

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)

传递此模型

-1
投票

在 SwiftUI 中,视图结构已经是视图模型(你不需要自己的对象),像选择这样的动态视图数据应该是与模型分离的

@State
,例如

@State var selectedCategory: Category?

var body: some View {
    List(selection: $selectedCategory) {
        ForEach(model.categories) {
    ...
选择更改时会调用

body

© www.soinside.com 2019 - 2024. All rights reserved.