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

SwiftUI自定义进度按钮点击时文本延迟消失问题求助

SwiftUI加载按钮文本延迟消失问题解决

我在SwiftUI中创建了一个点击后进入加载状态的按钮,代码如下:

private struct Button1: View {
    @State var isLoading = false

    var body: some View {
            Button("Hello") {
                isLoading.toggle()
            }
            .buttonStyle(ProgressButtonStyle(isLoading: $isLoading))
    }
}

同时为该按钮的加载状态创建了自定义按钮样式:

struct ProgressButtonStyle: PrimitiveButtonStyle {
    @Binding var isLoading: Bool
    @Environment(\.isEnabled) var isEnabled

    func makeBody(configuration: PrimitiveButtonStyleConfiguration) -> some View {
        Button(action: { dispatchTrigger(configuration) }) {
            configuration
                .label
                .opacity(contentOpacity)
        }
        .buttonStyle(.base)
        .overlay(progressViewOverlay())
    }

    @ViewBuilder func progressViewOverlay() -> some View {
        isLoading ? ProgressView().tint(progressTintColor) : nil
    }

    var progressTintColor: Color {
        if isEnabled {
            return .white
        } else {
            return Color(.tertiaryLabel)
        }
    }

    var contentOpacity: Double {
        isLoading ? 0.0 : 1.0
    }

    private func dispatchTrigger(_ configuration: Configuration) {
        guard !isLoading else { return }
        configuration.trigger()
    }
}

当通过其他按钮控制该按钮的显示状态时一切正常,但直接点击该按钮时,按钮文本会出现延迟消失的情况。

问题原因

自定义样式内部嵌套了Button,这个内部按钮会触发SwiftUI默认的点击动画,该动画会干扰文本opacity的即时变化,导致延迟消失的现象。

解决方案

移除自定义样式内部嵌套的Button,直接基于configuration.label构建视图,通过onTapGesture处理点击事件,避免默认动画的干扰:

struct ProgressButtonStyle: PrimitiveButtonStyle {
    @Binding var isLoading: Bool
    @Environment(\.isEnabled) var isEnabled

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .opacity(contentOpacity)
            .overlay(progressViewOverlay())
            .buttonStyle(.base)
            .onTapGesture {
                dispatchTrigger(configuration)
            }
    }

    @ViewBuilder func progressViewOverlay() -> some View {
        if isLoading {
            ProgressView().tint(progressTintColor)
        }
    }

    var progressTintColor: Color {
        isEnabled ? .white : Color(.tertiaryLabel)
    }

    var contentOpacity: Double {
        isLoading ? 0.0 : 1.0
    }

    private func dispatchTrigger(_ configuration: Configuration) {
        guard !isLoading else { return }
        configuration.trigger()
    }
}

替代方案(快速修复)

如果不想修改结构,也可以给文本的opacity变化添加明确的动画,覆盖默认动画的影响:

// 在原自定义样式的configuration.label处修改
configuration.label
    .opacity(contentOpacity)
    .animation(.easeOut(duration: 0.2), value: isLoading)

内容的提问来源于stack exchange,提问作者Betul Aksu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:17:45