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

SwiftUI自定义Hide修饰符仅首次生效问题排查

问题分析与解决方案

你的问题核心在于:第一次触发隐藏动画后,Text视图并未从视图层级中移除(因为message始终是非空状态),再次点击按钮时,由于message值未发生变化(或变化未触发视图重建),Hide修饰符的onAppear不会再次执行,isVisible状态停留在false,导致后续无法正常显示。

解决方案1:添加动态ID强制重建视图

在调用hide修饰符的Text上绑定一个动态id,确保每次需要显示时,视图都会被重新创建,从而重置Hide修饰符的内部状态:

struct LoginScreenContainer: View {
    @State private var message: String = ""
    @State private var showTrigger = 0 // 新增触发计数
    
    var body: some View {
        NavigationStack {
            Button("Show Message") {
                showTrigger += 1 // 每次点击更新计数
                message = "Error Message"
            }
        }
        .overlay(alignment: .bottom) {
            if !message.isEmpty {
                Text("ERROR")
                    .id(showTrigger) // 绑定触发计数作为唯一标识
                    .hide(in: 2.0)
            }
        }
    }
}

解决方案2:优化Hide修饰符的生命周期逻辑

将onAppear替换为task,利用SwiftUI的任务生命周期特性,在视图重建或重新出现时自动重置状态:

struct Hide: ViewModifier {
    let delay: Double
    @State private var isVisible = true
    
    func body(content: Content) -> some View {
        content
            .opacity(isVisible ? 1 : 0)
            .task {
                // 任务启动时强制重置可见性
                isVisible = true
                // 延迟指定时长
                try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
                // 执行隐藏动画
                withAnimation {
                    isVisible = false
                }
            }
    }
}

extension View {
    func hide(in delay: Double) -> some View {
        modifier(Hide(delay: delay))
    }
}

配合方案1的showTrigger使用,即可实现每次点击都触发完整的显示-隐藏流程。

进阶优化:隐藏完成后自动清空消息

如果希望在隐藏动画结束后自动移除Text视图,可以给Hide修饰符添加隐藏完成回调:

struct Hide: ViewModifier {
    let delay: Double
    let onHidden: (() -> Void)? // 新增隐藏完成回调
    @State private var isVisible = true
    
    func body(content: Content) -> some View {
        content
            .opacity(isVisible ? 1 : 0)
            .task {
                isVisible = true
                try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
                withAnimation {
                    isVisible = false
                }
                onHidden?() // 触发回调
            }
    }
}

extension View {
    func hide(in delay: Double, onHidden: (() -> Void)? = nil) -> some View {
        modifier(Hide(delay: delay, onHidden: onHidden))
    }
}

使用时:

Text("ERROR")
    .id(showTrigger)
    .hide(in: 2.0) {
        message = "" // 隐藏完成后清空消息,移除视图
    }

内容的提问来源于stack exchange,提问作者john doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:48:15