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

如何为同一SwiftUI视图设置两种不同的条件转场效果?

解决SwiftUI中X按钮根据点击源应用不同移除转场的问题

问题根源

你遇到的转场延迟触发问题,核心原因是@State变量的更新与视图移除的状态变更属于同一批次的视图刷新。SwiftUI会先处理状态变更,再应用转场逻辑,导致当前移除操作无法获取到刚更新的transition值,只能在下一次视图刷新时生效。

解决方案

不需要用@State持久化转场样式,而是在触发移除操作时,通过Transaction直接为当前动画指定对应的转场效果,确保转场和视图移除操作同步生效。

修改后的完整代码

import Combine
import Foundation
import SwiftUI

extension String {
    var isNonEmpty: Bool { !isEmpty }
}

struct SearchBar: View {
    @Binding var searchField: String
    let clearSearchField: () -> Void
    let isFocus: Bool
    let setFocus: (Bool) -> Void

    var body: some View {
        HStack(spacing: 16) {
            ZStack {
                Color.gray
                VStack(alignment: .leading) {
                    HStack {
                        TextField(
                            "Placeholder",
                            text: $searchField,
                            onEditingChanged: { _ in setFocus(true) }
                        )
                        .foregroundColor(.white)

                        Spacer()

                        if searchField.isNonEmpty {
                            Button {
                                var transaction = Transaction(animation: .default)
                                transaction.transition = .opacity
                                withTransaction(transaction) {
                                    clearSearchField()
                                }
                            } label: {
                                Text("X")
                            }
                            .transition(.asymmetric(
                                insertion: .opacity,
                                removal: .identity
                            ))
                        }
                    }
                }
                .padding(16)
            }
            .cornerRadius(8)

            if isFocus {
                Button(
                    action: {
                        hideKeyboard()
                        var transaction = Transaction(animation: .default)
                        transaction.transition = .move(edge: .trailing).combined(with: .opacity)
                        withTransaction(transaction) {
                            clearSearchField()
                            setFocus(false)
                        }
                    },
                    label: {
                        Text("Cancel")
                            .foregroundColor(.white)
                    }
                )
                .transition(.move(edge: .trailing).combined(with: .opacity))
            }
        }
        .frame(height: 48)
        .foregroundColor(searchField.isEmpty ? .gray : .white)
        .padding(16)
        .animation(.default, value: isFocus)
    }

    private func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

关键修改点

  • 移除@State var transition:转场样式不需要持久化,每次操作时动态指定即可。
  • 用Transaction绑定转场与状态变更:在触发clearSearchField(导致X按钮消失)前,创建Transaction并设置对应转场,通过withTransaction包裹状态变更,确保当前移除动画使用指定转场。
  • X按钮转场配置:将移除转场设为.identity,表示使用事务中指定的转场样式,插入转场保留原有opacity动画。
  • 修复颜色命名:将原代码中的.grey改为SwiftUI标准命名.gray。

修改后,点击X按钮时,X按钮会立即以opacity效果淡出;点击Cancel按钮时,X按钮会立即以move+opacity的组合效果滑出,完全符合预期行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:33:36