You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义DisclosureGroup中ForEach内容的条件显隐动画实现求助

自定义DisclosureGroup中ForEach内容的条件显隐动画实现求助

嘿,我完全懂你现在的困扰——自己折腾了个自定义DisclosureGroup,视觉样式和交互逻辑都调得很满意,结果卡在内容展开收起的动画上,明明功能正常,就是没有丝滑的过渡效果,对吧?我之前做类似自定义组件的时候也踩过这个坑,给你分享几个亲测有效的解决方案,你可以照着试试:

方案一:用Transition配合状态动画

这个方案是给内容区域指定显隐时的过渡效果,同时在触发状态切换时包裹动画上下文,让SwiftUI自动处理动画流程。你可以把原代码里的内容部分做如下修改:

struct SectionView: View {
    let title: String
    let data: [String]
    
    @State private var isExpanded: Bool = true
    
    var body: some View {
        VStack(spacing: 12) { // 加个间距让布局更舒服
            HStack {
                Text(title)
                    .font(.title2)
                    .fontWeight(.semibold)
                Text("\(data.count)")
                    .font(.body)
                    .fontWeight(.light)
                    .foregroundStyle(.secondary)
                Spacer()
                Button {
                    // 用withAnimation包裹状态切换,让所有依赖该状态的视图触发动画
                    withAnimation(.easeInOut(duration: 0.3)) {
                        isExpanded.toggle()
                    }
                } label: {
                    Image(systemName: isExpanded ? "chevron.down" : "chevron.right")
                        .foregroundColor(.secondary)
                        // 给箭头也加个旋转动画,和内容动画呼应
                        .rotationEffect(.degrees(isExpanded ? 0 : -90))
                }
            }
            
            // 内容区域:用if控制显隐,同时指定过渡动画
            if isExpanded {
                VStack(alignment: .leading, spacing: 8) {
                    ForEach(data, id: \.self) { item in
                        Text(item)
                            .font(.body)
                            .padding(.vertical, 4)
                            .padding(.horizontal, 8)
                    }
                }
                // 自定义过渡效果:滑入+淡入,消失时反向执行
                .transition(.opacity.combined(with: .slide))
                // 绑定状态变化,确保动画精准触发
                .animation(.easeInOut(duration: 0.3), value: isExpanded)
            }
        }
        .padding(.horizontal)
    }
}

方案二:高度渐变动画(更贴近原生DisclosureGroup效果)

原生DisclosureGroup的展开收起其实是高度从0到自动高度的渐变动画,你可以用frame的height属性来模拟这个效果,过渡会更自然:

struct SectionView: View {
    let title: String
    let data: [String]
    
    @State private var isExpanded: Bool = true
    
    var body: some View {
        VStack(spacing: 12) {
            HStack {
                // 这里的HStack内容和方案一完全一致
                Text(title)
                    .font(.title2)
                    .fontWeight(.semibold)
                Text("\(data.count)")
                    .font(.body)
                    .fontWeight(.light)
                    .foregroundStyle(.secondary)
                Spacer()
                Button {
                    withAnimation(.easeInOut(duration: 0.3)) {
                        isExpanded.toggle()
                    }
                } label: {
                    Image(systemName: isExpanded ? "chevron.down" : "chevron.right")
                        .foregroundColor(.secondary)
                        .rotationEffect(.degrees(isExpanded ? 0 : -90))
                }
            }
            
            // 内容区域:用frame高度控制显隐+裁剪溢出
            VStack(alignment: .leading, spacing: 8) {
                ForEach(data, id: \.self) { item in
                    Text(item)
                        .font(.body)
                        .padding(.vertical, 4)
                        .padding(.horizontal, 8)
                }
            }
            // 隐藏时高度设为0,展开时自动适应内容高度
            .frame(height: isExpanded ? .infinity : 0, alignment: .top)
            .clipped() // 裁剪高度为0时的内容溢出,避免出现残影
            .animation(.easeInOut(duration: 0.3), value: isExpanded)
        }
        .padding(.horizontal)
    }
}

小提醒

  • 不要把动画修饰符加在根VStack上,不然整个组件(包括标题栏)都会跟着动,显得很奇怪,精准加在需要动画的内容区域就好;
  • 如果你的内容是动态加载的,比如数据会变化,记得把ForEach的id设置成稳定的唯一标识(如果用\.self的话要确保data里的字符串不会重复);
  • 可以调整.easeInOut(duration: 0.3)里的时长参数,找到最贴合你UI风格的动画速度。

你可以根据自己想要的视觉效果选其中一种方案,或者把两种结合起来调整,应该就能解决动画的问题啦!

备注:内容来源于stack exchange,提问作者Johnathon Sullinger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 13:00:30