按钮点击时图标动画异常问题排查
按钮选择时图标动画异常排查
问题现象
使用AppScreen枚举管理应用内页面的标题、图标信息,通过ForEach遍历枚举值生成菜单按钮。选中「Button 1」时图标动画正常,但选中「Button 2」时,「Button 1」和「Button 2」的图标会同时触发动画,而非仅当前选中的按钮。
相关代码
AppScreen 枚举
import Foundation enum AppScreen: CaseIterable, Identifiable { case btn1, btn2 var id: AppScreen { self } var screenName: String { switch self { case .btn1: "btn1" case .btn2: "btn2" } } var screenIcon: String { switch self { case .btn1: "exclamationmark.arrow.triangle.2.circlepath" case .btn2: "person.2" } } }
AppSideBar 视图
struct AppSideBar: View { @Binding var appScreen: AppScreen? @State private var selection: String = "" var body: some View { HStack { VStack { ForEach(AppScreen.allCases) { screen in Button { selection = screen.screenName } label: { VStack { Image(systemName: screen.screenIcon) Text(screen.screenName) .foregroundStyle(selection == screen.screenName ? .red : .white) } } .symbolEffect(.bounce, value: selection == screen.screenName) .buttonStyle(.borderless) } } } #if os(macOS) .frame(width: NSScreen.main!.visibleFrame.width / 1.6, height: NSScreen.main!.visibleFrame.height - 100) #endif } }
问题原因及修复
核心问题
symbolEffect的触发值使用了selection == screen.screenName这个布尔表达式。当从「btn1」切换到「btn2」时,两个按钮的布尔值都会发生变化:原选中的btn1从true变为false,新选中的btn2从false变为true。SwiftUI会同时响应这两个值的变化,导致两个图标都触发动画。
修复方案
方案1:改用选中字符串作为触发值
将symbolEffect的触发值改为selection本身,只有当选中值变化时,匹配当前按钮的标识才会触发动画:
struct AppSideBar: View { @Binding var appScreen: AppScreen? @State private var selection: String = "" var body: some View { HStack { VStack { ForEach(AppScreen.allCases) { screen in Button { selection = screen.screenName } label: { VStack { Image(systemName: screen.screenIcon) Text(screen.screenName) .foregroundStyle(selection == screen.screenName ? .red : .white) } } // 触发值改为selection,仅当选中值变化时,匹配的按钮触发动画 .symbolEffect(.bounce, value: selection) .buttonStyle(.borderless) } } } #if os(macOS) .frame(width: NSScreen.main!.visibleFrame.width / 1.6, height: NSScreen.main!.visibleFrame.height - 100) #endif } }
方案2:直接绑定枚举类型(更推荐)
用AppScreen枚举类型代替字符串存储选中状态,避免字符串匹配的潜在错误,同时精准控制动画触发:
struct AppSideBar: View { @Binding var appScreen: AppScreen? // 将selection改为AppScreen可选类型,默认无选中状态 @State private var selection: AppScreen? = nil var body: some View { HStack { VStack { ForEach(AppScreen.allCases) { screen in Button { selection = screen } label: { VStack { Image(systemName: screen.screenIcon) Text(screen.screenName) .foregroundStyle(selection == screen ? .red : .white) } } // 仅当当前按钮变为选中状态时触发动画 .symbolEffect(.bounce, value: selection == screen, options: .triggerOnAppear) .buttonStyle(.borderless) } } } #if os(macOS) .frame(width: NSScreen.main!.visibleFrame.width / 1.6, height: NSScreen.main!.visibleFrame.height - 100) #endif } }
该方案中,切换选中项时只有新选中的按钮会触发selection == screen从false到true的变化,结合options: .triggerOnAppear参数,可确保仅当前选中按钮播放动画。
内容的提问来源于stack exchange,提问作者kAiN
相关产品推荐
相关产品推荐

