SwiftUI:条件修改字体粗细时界面出现卡顿问题
解决SwiftUI字体粗细切换时的卡顿问题
你的问题本质是不同字重的字体度量差异触发了布局重算,加上spring动画的放大效果,导致视觉上出现卡顿/跳动。以下是几个实用的解决办法:
1. 固定文本容器宽度(最直接有效)
不同字重的文本宽度不同,切换时会让父视图重新计算布局,这是卡顿的核心原因。给Text添加固定宽度的frame,就能避免布局变化:
Text(tab) .font(.system(size: 16, weight: selectedTab == tab ? .heavy : .medium, design: .rounded)) .foregroundColor(selectedTab == tab ? .red : .blue) .frame(width: 60, alignment: .leading) // 根据你的文本长度调整宽度 .onTapGesture { withAnimation(.spring()) { selectedTab = tab } }
如果想适配不同文本长度,可以提前计算所有标签的最大宽度:
private var maxTabWidth: CGFloat { tbs.map { tab in let heavyFont = UIFont.systemFont(ofSize: 16, weight: .heavy, design: .rounded) return tab.size(withAttributes: [.font: heavyFont]).width }.max() ?? 0 }
然后把frame的width设为maxTabWidth即可。
2. 优化下划线的显示逻辑
原来的if tab == selectedTab会直接添加/移除下划线视图,也会触发布局变化。改用透明度动画替代条件显示,过渡更平滑:
Rectangle() .foregroundColor(.red) .frame(width: 20, height: 1) .opacity(selectedTab == tab ? 1 : 0)
完整修改后的代码
struct Playground: View { @State private var selectedTab = "Liked" let tbs = ["Liked", "Reading", "Ended"] private var maxTabWidth: CGFloat { tbs.map { tab in let heavyFont = UIFont.systemFont(ofSize: 16, weight: .heavy, design: .rounded) return tab.size(withAttributes: [.font: heavyFont]).width }.max() ?? 0 } var body: some View { HStack { ForEach(tbs, id: \.self) { tab in HStack { VStack(alignment: .leading, spacing: 1) { Text(tab) .font(.system(size: 16, weight: selectedTab == tab ? .heavy : .medium, design: .rounded)) .foregroundColor(selectedTab == tab ? .red : .blue) .frame(width: maxTabWidth, alignment: .leading) .onTapGesture { withAnimation(.spring()) { selectedTab = tab } } .padding(.bottom, selectedTab != tab ? 1 : 0) Rectangle() .foregroundColor(.red) .frame(width: 20, height: 1) .opacity(selectedTab == tab ? 1 : 0) } } .padding(.horizontal, 5) } } } }
这些修改能彻底消除字体切换时的卡顿,同时保持动画的流畅性。
内容的提问来源于stack exchange,提问作者N G
相关产品推荐
相关产品推荐

