SwiftUI 中为何 DisclosureGroup 视图在收缩时没有动画效果?

概览

小伙伴们肯定都惊叹过 SwiftUI 在界面设计中摧枯拉朽和辞简理博的强大,也一定被 SwiftUI 开发中各种"恢诡谲怪"的奇葩问题搞的"五内俱崩"、欲哭无泪。

在这些种种问题中,动画效果不尽如人意是很常见的一类。

如上图所示,我们的 DisclosureGroup 在收缩时并没有对应"可耐"的动画,显得很生硬!这是怎么回事呢?

在本篇博文中,您将学到如下内容:

  1. SwiftUI 中的 DisclosureGroup 视图
  2. "动画是个好东西,可惜你没有"
  3. 彗汜画涂,一发入魂!

在 SwiftUI 中,动画效果不能如愿大概率是秃头码农们自己撸码的不是。我们必须知其然同时知其所以然才能泰然处之的将它们"全面瓦解"。

那还等什么呢?Let's fix it!!!;)


SwiftUI 中的 DisclosureGroup 视图

从 SwiftUI 2.0(iOS 14)开始,苹果增加了全新的 DisclosureGroup 原生视图:

DisclosureGroup 的作用很简单:就是节省空间。我们可以通过它来有的放矢的展开和收起内容视图,从而合理的精简界面布局。

swift 复制代码
struct ContentView: View {
    
    @State var isExpanding = false
    @State var sltingValue: Int?
    
    var body: some View {
        NavigationStack {
            Form {
                DisclosureGroup(
                    isExpanded: $isExpanding.animation(),
                    content: {
                        List {
                            ForEach(0...10, id: \.self) { i in
                                Text("Value \(i)")
                                    .font(.title2.weight(.bold))
                                    .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                                        Button("选择") {
                                            sltingValue = i
                                            isExpanding = false
                                        }
                                        .tint(.green)
                                    }
                            }
                            Button("取消选择") {
                                sltingValue = nil
                                isExpanding = false
                            }
                        }
                    },
                    label: {
                        VStack(alignment: .leading) {
                            if let sltingValue {
                                Text("当前选择值:\(sltingValue)")
                                    .font(.title)
                            }
                            Spacer()
                            Text("当前收展状态:[\(isExpanding ? "展开" : "收起")]")
                        }
                    }
                )
            }
            .toolbar {
                Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
                    .font(.headline)
                    .foregroundStyle(.gray)
            }
            .animation(.bouncy, value: isExpanding)
        }
    }
}

在如上代码中,我们利用 DisclosureGroup 视图控制着冗长列表内容的显示和隐藏。可以看到,我们实际是用 isExpanding 状态来反映且驱动 DisclosureGroup 收展状态的。

"动画是个好东西,可惜你没有"

现在我们已初步知晓了 DisclosureGroup 的作用,那么回到博文开头那个例子中去:为什么示例中 DisclosureGroup 的收缩没有动画点缀呢?

要探索和回答这个问题,我们有必要先来看看示例的源代码:

swift 复制代码
DisclosureGroup(isExpanded: $isCheckInSelectPanelExpanding.animation(.bouncy), content: {
    HStack {
        DatePicker("", selection: $pickingTime, displayedComponents: [.hourAndMinute])
        
        Button(action: {
            isCheckInSelectPanelExpanding = false
            
            withAnimation {
                checkInTime = pickingTime
            }
        }) {
            Text("确定")
        }
        .buttonStyle(.borderedProminent)
        
        Button(action: {
            isCheckInSelectPanelExpanding = false
            
            withAnimation {
                checkInTime = nil
            }
        }) {
            Text("取消打卡")
        }
        .buttonStyle(.borderedProminent)
        .tint(Color.red)
        .disabled(checkInTime == nil)
    }
    
}, label: {
    HStack {
        Text("规定打卡时间")
        Spacer()
        Text("\(checkInTime == nil ? "无" : V2_Model.hmFormater.string(from: checkInTime!))")
            .foregroundStyle(checkInTime == nil ? .gray : .primary)
    }
})

可以看到上面代码和之前几乎如出一辙:我们是用 isCheckInSelectPanelExpanding 状态来驱动 DisclosureGroup 的展开和收缩。我们在 DisclosureGroup 展开按钮的 action 中知趣的将 isCheckInSelectPanelExpanding 设置为 false,这样在完成按钮的功能之后立即收起 DisclosureGroup 有助于节省用户宝贵的时间。

但是为什么 DisclosureGroup 收起动画却不翼而飞了呢?

彗汜画涂,一发入魂!

仔细观察示例代码中的逻辑可以发现,当 isCheckInSelectPanelExpanding 被置为 false 时,就会导致 DisclosureGroup 的内容视图(Content View)从界面上被"清除"掉。

如果在一个即将被"删除"的视图上应用动画,那么多半人家不会鸟你。

所以,我们要做的就是让 SwiftUI 系统明确知道我们需要 isCheckInSelectPanelExpanding 状态的改变产生动画效果。

怎么操作呢?其实很简单:我们只需将 isCheckInSelectPanelExpanding 放到显式动画块中即可。

swift 复制代码
DisclosureGroup(isExpanded: $isCheckInSelectPanelExpanding.animation(.bouncy), content: {
    HStack {
        DatePicker("", selection: $pickingTime, displayedComponents: [.hourAndMinute])
        
        Button(action: {
            
            withAnimation {
                checkInTime = pickingTime
                isCheckInSelectPanelExpanding = false
            }
        }) {
            Text("确定")
        }
        .buttonStyle(.borderedProminent)
        
        Button(action: {
            
            withAnimation {
                checkInTime = nil
                isCheckInSelectPanelExpanding = false
            }
        }) {
            Text("取消打卡")
        }
        .buttonStyle(.borderedProminent)
        .tint(Color.red)
        .disabled(checkInTime == nil)
    }
    
}, label: {
    HStack {
        Text("规定打卡时间")
        Spacer()
        Text("\(checkInTime == nil ? "无" : V2_Model.hmFormater.string(from: checkInTime!))")
            .foregroundStyle(checkInTime == nil ? .gray : .primary)
    }
})

更多关于 SwiftUI 显式和隐式动画的介绍,请小伙伴们移步如下链接观赏:


在将 isCheckInSelectPanelExpanding 状态的改变改为显式动画后,我们再来运行看一下效果:

现在丝般顺滑的动画又一次"大圣归来",我们可以继续我们的 App 开发"仲夏夜"之梦啦,棒棒哒!💯

总结

在本篇博文中,我们介绍了 SwiftUI 2.0 新增的 DisclosureGroup 原生视图,并接着讨论了为什么它的收起操作没有动画,并最后给出解决方案。

感谢观赏,再会!8-)

相关推荐
大熊猫侯佩12 小时前
Swift 6.4 的 Ref / MutableRef 大揭秘:给值类型开一扇“安全的小窗”
ios·swift·编程语言
大熊猫侯佩1 天前
WWDC26 SwiftUI 进化之路:砸碎黑盒,彻底迎来开发自由!
ios·swiftui·swift
游戏开发爱好者81 天前
iPhone真机调试有哪些方法?一次定位推送权限问题时整理出来的几种方案
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
大熊猫侯佩1 天前
WWDC26 最被忽视的王炸:告别“伪并发”陷阱,Swift 6.4 的 async defer
ios·swift·编程语言
Flywith242 天前
开源项目:把本地视频转成 Live Photo 并导入 Apple Photos
app·mac·apple
92year3 天前
Xcode 27 AI Agent 实测:苹果把 Claude、ChatGPT、Gemini 都塞进了 IDE
agent·ai编程·xcode·apple·wwdc
宜昌未来智慧谷3 天前
WWDC 2026开发者视角解读:Siri独立App的技术架构与第三方AI模型接入机制
人工智能·架构·apple·wwdc·gemini
人月神话-Lee4 天前
WWDC26 深度解析:如何在 iOS 27 中打造“秒开”的相机体验
ios·swift·相机·wwdc·用户体验
Tr2e4 天前
🐱 从 0 到 1:用 Swift 手搓一个 macOS 桌面宠物(附源码)
macos·ios·swift
REDcker4 天前
WWDC2026系统更新综述
macos·ios·开发者·apple·wwdc·ipados·wwdc2026