在 Swiftui 示例中修改 isFavourite 值

Posted

技术标签:

【中文标题】在 Swiftui 示例中修改 isFavourite 值【英文标题】:Modifying the isFavourite value in Swiftui example 【发布时间】:2021-01-20 15:57:03 【问题描述】:

来自教程https://developer.apple.com/tutorials/swiftui/handling-user-input,它有星号表示“isFavourite”。

我已将星号更改为按钮,如果用户点击它,它将切换 isFavourite 值。

struct LandmarkRow: View 

    @State var landmark: Landmark
    @State var isChecked: Bool = false

    var body: some View 
        HStack 
            landmark.image
                .resizable()
                .frame(width: 50, height: 50)
            Text(landmark.name)

            Spacer()

            Button(action: 
                self.isChecked.toggle()
                landmark.isFavourite.toggle() // tried to modify the landmark value
            , label: 
                if self.isChecked 
                    Image(systemName: "checkmark.square.fill")
                        .imageScale(.large)
                 else 
                    Image(systemName: "square")
                        .imageScale(.large)
                
            )
        
    

但是,我在这里发现了一个问题。

回到LandmarkList 视图,我想计算有多少地标被选中。但是,当我使用for循环遍历地标时,我发现isFavourite值没有被修改。

struct LandmarkList: View 
    var body: some View 
        NavigationView 
            VStack 

            Text("\(getCount(landmarks)") // fail here, it shows 0 forever
            List(landmarks)  landmark in
                NavigationLink(destination: LandmarkDetail(landmark: landmark)) 
                    LandmarkRow(landmark: landmark)
                
            
            
            .navigationTitle("Landmarks")
        
    

我在 LandmarkList 中有一个函数 getCount:

func getCount(landmarks: [Landmark]) -> Int 
    var count: Int = 0
    for landmark in landmarks 
        if landmark.isFavourite 
           count += 1
        
    
    return count

那是因为@State 吗?或者这里出了什么问题?

【问题讨论】:

您需要将绑定传递给您的 LandmarkRow,因为您只更新行中最喜欢的值,而不是提供该行的实际数据 一个好的提示是始终将@State 属性声明为private。您不应该从LandmarkRow 视图之外以@State 的身份访问landmark 这是否回答了您的问题***.com/a/59316596/12299030? 【参考方案1】:

正如我在上面的评论中所说,您实际上并没有更新地标的全局状态,只是更新行中存在的状态,您需要使用绑定或访问 ModelData 中的值,即在环境中举行。

如果您查看LandmarkDetail 页面,那里有一个解决方案。

您可以将FavoriteButtonenvironmentObject 添加到ModelData,以及计算属性以计算您正在使用的地标的索引到LandmarkRow。这与LandmarkDetail中使用的解决方案基本相同。

这给出了以下内容:

struct LandmarkRow: View 
    // Access the modelData from the environment
    @EnvironmentObject var modelData: ModelData
    var landmark: Landmark

    // We need to get the index so that we can update the landmark in the ModelData
    var landmarkIndex: Int 
        modelData.landmarks.firstIndex(where:  $0.id == landmark.id )!
    

    var body: some View 
        HStack 
            landmark.image
                .resizable()
                .frame(width: 50, height: 50)
            Text(landmark.name)

            Spacer()
            // Use the favorite button that already exists in the project
            FavoriteButton(isSet: $modelData.landmarks[landmarkIndex].isFavorite)
                .buttonStyle(PlainButtonStyle()) 
            // As LandmarkRow is used in a NavigationLink we need to set the 
            // buttonStyle so that we can use it otherwise we will just navigate.
        
    


struct LandmarkRow_Previews: PreviewProvider 
    static var landmarks = ModelData().landmarks

    static var previews: some View 
        Group 
            LandmarkRow(landmark: landmarks[0])
            LandmarkRow(landmark: landmarks[1])
        
        .previewLayout(.fixed(width: 300, height: 70))
        .environmentObject(ModelData()) // Add the environmentObject so the previews will work
    

然后,如果您想查看有多少收藏夹,可以在 LandmarkList 中执行以下操作

var body: some View 
    NavigationView 
        List 
            Text("Favorites: \(modelData.landmarks.filter($0.isFavorite).count)")
            Toggle(isOn: $showFavoritesOnly) 
                Text("Favorites only")
            

            ForEach(filteredLandmarks)  landmark in
                NavigationLink(destination: LandmarkDetail(landmark: landmark)) 
                    LandmarkRow(landmark: landmark)
                
            
        
        .navigationTitle("Landmarks")
    

这些更改产生以下结果:

在 Xcode 12.3 和 ios 14.3 中测试

【讨论】:

以上是关于在 Swiftui 示例中修改 isFavourite 值的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI ViewModifier - 添加字距调整

如何在 SwiftUI 的 PreviewProvider 中编写示例模型对象?

是否可以使用 SwiftUI 在 TextField 上设置字符限制?

在 SwiftUI 中修改 List 的背景颜色?

SwiftUI:ListItem 手势

SwiftUI - 如何修改数组中的文本字段