Xcode提供了一个新的SwiftUI模板:SwiftUI分析工具,用于跟踪视图body的调用、DynamicView属性随时间的更新以及识别较慢的帧。
1. 打开Profile。
2. 选择SwiftUI。
3. 启动模拟器。
4. 启动后,时间轴面板中会显示body调用的实时分析。
5. 点击停止按钮,关闭模拟器。
从列表可以看出分析工具对视图body的调用、视图的属性、动画的提交以及时间的开销分析和统计。
6. 将统计信息从上往下看,在body属性里花费7.34微秒,创建了ScrollView组成。由于动画的原因,属性更新了5次。CPU使用率为51%。
7. 由于点击了界面顶部的标签,所以显示使用24.93微妙来刷新TopMenuView,相关的视图为 Button,更新了6次的属性,CPU使用率为60%。
8. 在视图列表中,显示了界面里的各视图的数量和消耗的性能。例如总共使用了24个Button,共花费486.04微秒。点击下拉箭头,显示项目里的自定义视图列表。
9. 此时显示了您自定义的视图的列表,例如共在界面中使用了8个自定义的CircleView,共花费83.44微秒。
10. 点击此处的箭头,显示项目里的自定义视图列表。
11. 从列表可以看出视图创建的顺序、花费的时间。最先创建ContentView,共花费130.28微秒。接着创建CountryListView,花费116.19微秒。
12. 点击此处的时序摘要命令,返回原来的时序列表。
13. 点击Slow.body标签,显示body里的渲染相对较慢的视图。
14. 在时间轴上点击,查看此处的视图信息。
15. 从弹出的信息摘要可以看出,TopMenuView在此处的时间段里渲染较慢,共花费24.93微秒, CPU使用率为120%。接着点击查看渲染较慢的帧。
16. 从时间轴里可以识别渲染较慢的帧,点击查看此处的帧信息。
17. 从弹出的信息摘要可以看出,DemoProject的初始化影响了帧速,共花费59.08毫秒,CPU使用率为182%。
18. 点击此处的选项,可以查看在播放动画时,视图属性的刷新。
19. 在时间轴上点击,查看此时段的视图属性的刷新。
20. 在项目上点击鼠标右键,可以打开上下文菜单。
21. 选择此处的设置检查范围和缩放命令,可以查看状态变更的顺序和数值。
22. 在时间轴上点击,查看此处属性的变化。
23. 从信息摘要可以看出,在此时段的状态变化是:CircleView的一个属性的值由false变为true。在完成项目的众多因素的分析之后,点击此处的关闭按钮。
24. 最后点击此处的不要保存按钮,退出分析工具界面。
源码:
import SwiftUI
struct ContentView : View
{
@State var isAnimating = false
var body: some View
{
ZStack
{
Rectangle()
.fill(LinearGradient(gradient: Gradient.init(colors: [Color.init(red: 55/255, green: 67/255, blue: 109/255),Color.init(red: 54/255, green: 98/255, blue: 127/255)]), startPoint: .top, endPoint: .bottom))
.edgesIgnoringSafeArea(.all)
VStack
{
TopBarView()
.opacity(isAnimating ? 1 : 0)
.animation(Animation.spring().delay(0))
SplitterView()
.opacity(isAnimating ? 1 : 0)
.animation(Animation.spring().delay(0.2))
TopMenuView()
.opacity(isAnimating ? 1 : 0)
.animation(Animation.spring().delay(0.4))
PieChartView()
.opacity(isAnimating ? 1 : 0)
.animation(Animation.spring().delay(0.6))
CountryListView()
.opacity(isAnimating ? 1 : 0)
.animation(Animation.spring().delay(0.8))
}
}
.onAppear
{
self.isAnimating.toggle()
}
}
}
struct CircleView: View
{
var diameter = chartWidth
var color = activeColor
var startPoint : CGFloat = 0
var endPoint : CGFloat = 0.5
var angle = -45.0
@State var isAnimation = false
var body: some View
{
ZStack
{
Circle()
.stroke(lineWidth: 2)
.fill(inactiveColor)
.frame(width:diameter, height:diameter)
Circle()
.trim(from: isAnimation ? startPoint : 0, to: isAnimation ? endPoint : 1)
.stroke(lineWidth: 4)
.fill(color)
.frame(width:diameter, height:diameter)
.rotationEffect(.degrees(isAnimation ? angle : -720))
.animation(.easeInOut(duration: 3))
.onAppear {
self.isAnimation.toggle()
}
}
}
}
struct TopBarView: View
{
var body: some View
{
HStack
{
Image(systemName: "list.bullet.indent")
Spacer()
Text("STATISTICS")
.bold()
Spacer()
Image(systemName: "magnifyingglass")
}
.padding(.leading,30)
.padding(.trailing,30)
.foregroundColor(.white)
}
}
struct TopMenuView: View
{
@State private var currentIndex : Int = 1
var body: some View
{
VStack
{
HStack
{
Button(action:
{
self.currentIndex = 1
}){
Text("TODAY")
.font(.system(size: regularFontSize))
.frame(width: menuWidth, height: 40)
.foregroundColor(self.currentIndex == 1 ? activeColor : regularColor)
}
Button(action:
{
self.currentIndex = 2
}){
Text("WEEK")
.font(.system(size: regularFontSize))
.frame(width: menuWidth, height:40)
.foregroundColor(self.currentIndex == 2 ? activeColor : regularColor)
}
Button(action:
{
self.currentIndex = 3
}){
Text("MONTH")
.font(.system(size: regularFontSize))
.frame(width: menuWidth, height:40)
.foregroundColor(self.currentIndex == 3 ? activeColor : regularColor)
}
Button(action:
{
self.currentIndex = 4
}){
Text("ALL TIME")
.font(.system(size: regularFontSize))
.frame(width: menuWidth, height:40)
.foregroundColor(self.currentIndex == 4 ? activeColor : regularColor)
}
}
ZStack(alignment: .leading)
{
SplitterView()
Rectangle()
.fill(activeColor)
.frame(width: UIScreen.main.bounds.width/4, height:1)
.offset(x: menuWidth * CGFloat(currentIndex-1), y: 0)
.animation(.spring())
}
}
}
}
struct PieChartView: View
{
var body: some View
{
ZStack
{
CircleView()
CircleView(diameter: chartWidth - 40, color: .purple, startPoint: 0, endPoint: 0.4, angle: 70)
CircleView(diameter: chartWidth - 80, color: .green, startPoint: 0, endPoint: 0.3, angle: 190)
CircleView(diameter: chartWidth - 120, color: .yellow, startPoint: 0, endPoint: 0.2, angle: 135)
VStack
{
Text("1383")
.font(.system(size: 32))
Text("Visits")
.font(.system(size: 14))
.foregroundColor(Color.white.opacity(0.5))
}
.foregroundColor(.white)
}
.padding(.top, 30)
.padding(.bottom, 30)
}
}
struct CountryListView: View
{
@State var isAnimating = false
var body: some View
{
ScrollView
{
VStack
{
ForEach(0 ..< 7)
{ i in
VStack
{
HStack
{
Circle()
.fill(colors[i])
.frame(width: 10, height: 10)
Text(titles[i])
Spacer()
Text("\(visits[i])")
}
.padding(.leading, 30)
.padding(.trailing, 30)
.padding(.top, 10)
.padding(.bottom, 10)
.foregroundColor(.white)
.opacity(self.isAnimating ? 1 : 0)
.animation(Animation.spring().delay(Double(i) * 0.2 + 0.8))
Rectangle()
.fill(inactiveColor)
.frame(width:UIScreen.main.bounds.width - 40, height:1)
}
}
}
.onAppear
{
self.isAnimating.toggle()
}
}
}
}
struct SplitterView: View
{
var body: some View
{
Rectangle()
.fill(inactiveColor)
.frame(width: UIScreen.main.bounds.width, height:1)
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider
{
static var previews: some View
{
ContentView()
}
}
#endif