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-)

相关推荐
东坡肘子5 天前
SPI 加入 Apple,Swift 迈向自举 -- 肘子的 Swift 周报 #142
人工智能·swiftui·swift
神奇的程序员10 天前
开发了一个进阶版Apple健康
swiftui·apple·apple watch
东坡肘子12 天前
Swift 还让你 Excited 吗?-- 肘子的 Swift 周报 #141
人工智能·swiftui·swift
sweet丶14 天前
Swift 元编程-Macro
swift
武子康14 天前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
影寂ldy18 天前
WinForm PictureBox控件 + ImageList组件 完整笔记
开发语言·笔记·swift
Deepzz18 天前
macOS 上调教第三方鼠标的一些经验:从滚动顺滑到输入法自动切换
macos·swift·鼠标
大熊猫侯佩19 天前
WWDC26:SwiftUI 8 的 @State 全新“懒加载”机制与最佳实践
性能优化·swiftui·observable·懒加载·state·swift宏·实例初始化
东坡肘子19 天前
WWDC 26:AI 帮你看完了,然后呢?-- 肘子的 Swift 周报 #140
人工智能·swiftui·swift
大熊猫侯佩20 天前
SwiftData 迁移深度指南:从入门到“填坑”(下集)
数据库·swift·编程语言