You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按钮点击时图标动画异常问题排查

按钮选择时图标动画异常排查

问题现象

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:19:49