我有一个标签列表,当你 Select 它们时,芯片将出现.

当你点击每个芯片上的‘X’时,标签应该会滑出一个动画,列表中的标签将被标记为未选中.

我遇到的问题是,当我移除最后一个筹码时,标签左边的圆圈/圆圈判断与动画不顺畅.

我相信这是因为图标在被选中和未被选中时会发生变化,因为如果我保持图标不变,这不是问题.如果我删除芯片上的幻灯片动画也不是问题,但我喜欢这个动画并想保留它.

实际上,我在应用程序中的一些地方遇到了这个问题,涉及动画+更改图标,我想知道有没有解决这个问题的办法?

Gif of issue with animation

我在下面附上了一个可重现的例子.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Icon_Animation()
    }
}

struct Icon_Animation: View {
    //All tags
    var testTags: [Tag] =
    [Tag("tag1"),
     Tag("tag2"),
     Tag("tag3")]
    
    //Only tags that have been selected
    @State var selectedTags = [Tag]()
    
    var body: some View {
        ScrollView{
            
            //Hstack of the tags that have been selected
            HStack{
                ForEach(selectedTags){ tag in
                        HStack(spacing: 0){
                            Button{
                                //Clicking on the X will remove from selectedTags array, and then make that tag's isSelected = false
                                withAnimation(.easeOut) {
                                    if let index = selectedTags.firstIndex(where: {$0.name == tag.name}){
                                        selectedTags.remove(at: index)
                                    }
                                }
                                
                                //PROBLEM: even though this statemnt isn't in the withAnimation block, it causes a weird behavior with the circle/check-circle icon
                                //If I remove the withAnimation statement from the above block, it works fine. However, I would like to keep the slide animation on the chips.
                                tag.isSelected = false
                                
                            }label:{
                                Image(systemName: "x.circle.fill")
                                    .font(.subheadline)
                                    .padding(.horizontal, 6)
                            }
                            
                            Image(systemName: "number")
                                .font(.footnote.weight(.bold))
                                .padding(.trailing, 2)
                            Text("\(tag.name)")
                                .font(.footnote)
                        }
                        .padding(.trailing, 20)
                        .padding(.vertical, 6)
                        .background(Color.blue.opacity(0.6), in: RoundedRectangle(cornerRadius: 14, style: .continuous))
                        .transition(.slide)
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
            
            //List of tags where you can select each tag to create a chip
            ForEach(testTags){ tag in
                TagView(tag: tag)
                    .onTapGesture {
                        tag.isSelected.toggle()
                        
                        if(tag.isSelected == true){
                            selectedTags.append(tag)
                        }
                    }
            }
            .padding()
        }
        .padding()
    }
}

class Tag: Identifiable, ObservableObject {
    var id = UUID()
    @Published var name: String
    @Published var isSelected = false
    
    init(_ name: String){
        self.name = name
    }

}

struct TagView: View {
    @ObservedObject var tag: Tag = Tag("test")
  
    var body: some View {
        ZStack{
            //Overlay for when tag is selected
            Rectangle()
                .fill(Color.purple.opacity(0.6))
                .edgesIgnoringSafeArea(.all)
                .cornerRadius(5)
                .opacity(tag.isSelected ? 1 : 0)
            
            HStack(spacing: 8){
                
                //PROBLEM!!: I want to use a different icon based on whether tag isSelected, but it's causing a hitch in the animation when switching
                if(tag.isSelected){
                    Image(systemName: "checkmark.circle.fill")
                        .font(.title2.weight(.light))
                }else{
                    Image(systemName: "circle")
                        .font(.title2.weight(.light))
                }
                
                Image(systemName: "number")
                    .font(.body.weight(.bold))
                
                Text(tag.name)
                    .font(.headline)
                    .fontWeight(.bold)
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            
        }
    }
}

推荐答案

我认为这是一个常见的问题,归根结底,父视图从If语句接收两个不同的子视图,由于它们是两个不同的视图,具有不同的ID,SwiftUI不知道如何在它们之间设置动画.

诀窍是只使用一个具有可变内容的子视图.您应该能够通过将TagView中生成图像的if...else...替换为初始化器中的三进制数:

Image(systemName: tag.isSelected ? "checkmark.circle.fill" : "circle" )
               .font(.title2.weight(.light))

Ios相关问答推荐

自定义组件中的通用型-无法访问特定于收件箱的属性

iOS中的分段拾取器—手柄点击已 Select 的项目

SwiftUI中ForEach和@ State数组的可能并发问题

我如何确保用户继续他们在应用程序中停止的地方?

如何在SwiftUI ScrollView中zoom 中心项目?

删除领域中的cartItem时出现问题

AVFoundation Camera推出变焦SWIFT

iOS 16.4,SwiftUI 底部工作表有时会忽略presentationDetents

为什么IOS多行文本输入在React原生纸张对话框中无法正常工作?

iOS - 判断开发者模式是否开启 (Swift)

如何在 Mac OS Ventura 中使用 Xcode 13

错误:无法根据成员environmentObject推断上下文基础

类型任何视图不能符合具有泛型的协议上的视图

如何在不支持并发的自动关闭中修复'async'调用?

SwiftUI - 在ForEach的每个元素之间自动添加分隔符

Xcode 4.1 致命错误:自构建预编译头文件后修改了 stdlib

测试目标 X 遇到错误(提前意外退出,操作从未完成 bootstrap - 不会try 重新启动

使用 Swift 在一个 ViewController 中强制横向模式

更新字段时,UITextField值已更改未触发

找不到开发者磁盘映像