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

相关推荐
HarderCoder1 天前
Swift 模式:解构与匹配的安全之道
swift
东坡肘子1 天前
Swift 官方发布 Android SDK | 肘子的 Swift 周报 #0108
android·swiftui·swift
iOS阿玮2 天前
苹果 Swift 安卓SDK上线,用一套 Swift 代码开发安卓 App 成为可能!
uni-app·app·apple
YGGP2 天前
【Swift】LeetCode 53. 最大子数组和
swift
2501_916008892 天前
用多工具组合把 iOS 混淆做成可复用的工程能力(iOS混淆|IPA加固|无源码混淆|Ipa Guard|Swift Shield)
android·开发语言·ios·小程序·uni-app·iphone·swift
胎粉仔2 天前
Swift 初阶 —— inout 参数 & 数据独占问题
开发语言·ios·swift·1024程序员节
HarderCoder2 天前
Swift 下标(Subscripts)详解:从基础到进阶的完整指南
swift
YGGP2 天前
【Swift】LeetCode 189. 轮转数组
swift
疯笔码良3 天前
【IOS开发】SwiftUI + OpenCV实现图片的简单处理(一)
opencv·ios·swiftui
JZXStudio3 天前
5.A.swift 使用指南
框架·swift·app开发