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

如何在SwiftUI中检测视图调整大小完成的时机?

如何在SwiftUI中检测视图调整大小完成的时机?

我完全懂你这种感受——用定时器来判断调整大小结束,总觉得有点“别扭”,不够贴合SwiftUI的原生设计思路。其实我们可以用SwiftUI自带的异步任务机制来替代定时器,不仅代码更简洁,还能和视图生命周期完美适配,完全是更地道的SwiftUI写法。

先说说你当前代码的小问题:虽然定时器能实现功能,但需要手动管理它的生命周期,而且你注释里提到的[weak self]没加上,这很容易造成循环引用隐患;另外Foundation的Timer和SwiftUI的视图更新逻辑结合得不够紧密,总有点“外来者”的感觉。

下面给你两种更原生的解决方案:

方案一:用Task替代Timer(基础版)

利用SwiftUI的Task异步任务和取消机制,来实现“等待用户停止调整大小”的逻辑:

import SwiftUI

struct ContentView: View {
    // 用Task来管理异步延迟任务,SwiftUI会自动处理其生命周期
    @State private var resizeTask: Task<Void, Never>?

    var body: some View {
        GeometryReader { geometryValue in
            Color.white
                .onChange(of: geometryValue.size) { newValue in
                    // 取消之前的任务——如果用户还在拖动调整,旧任务就不会触发回调
                    resizeTask?.cancel()
                    
                    // 启动新的异步任务,延迟一小段时间再执行结束逻辑
                    resizeTask = Task {
                        do {
                            // 延迟0.01秒,这个时间可以根据你的需求调整
                            try await Task.sleep(nanoseconds: 10_000_000)
                            // 确保任务没被取消(用户可能在等待期间继续调整大小)
                            guard !Task.isCancelled else { return }
                            didEndResizing(size: newValue)
                        } catch {
                            // 任务被取消时会进入这里,无需额外处理
                        }
                    }
                }
        }
        .padding()
    }
    
    private func didEndResizing(size: CGSize) {
        print("View resizing ended. Final size: \(size)")
    }
}

这个方案的优势:

  • 完全基于SwiftUI原生的异步API,和视图生命周期深度绑定,视图销毁时Task会自动被取消,不用手动清理
  • 取消旧任务的逻辑更简洁,避免了定时器反复创建销毁的麻烦
  • 用try await Task.sleep实现延迟,更符合Swift的异步编程范式

方案二:封装成ViewModifier(进阶复用版)

如果你的多个视图都需要这个功能,最好把逻辑封装成ViewModifier,这样任何视图都能直接调用,复用性拉满:

import SwiftUI

// 自定义Modifier,封装resize结束检测逻辑
struct OnResizeEndedModifier: ViewModifier {
    let action: (CGSize) -> Void
    let delay: TimeInterval
    @State private var resizeTask: Task<Void, Never>?
    
    func body(content: Content) -> some View {
        content
            .background {
                // 用透明背景的GeometryReader来监听尺寸变化,不影响原视图样式
                GeometryReader { geometry in
                    Color.clear
                        .onChange(of: geometry.size) { newValue in
                            resizeTask?.cancel()
                            resizeTask = Task {
                                try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
                                guard !Task.isCancelled else { return }
                                action(newValue)
                            }
                        }
                }
            }
    }
}

// 给View扩展一个便捷方法,调用更直观
extension View {
    func onResizeEnded(delay: TimeInterval = 0.01, perform action: @escaping (CGSize) -> Void) -> some View {
        modifier(OnResizeEndedModifier(action: action, delay: delay))
    }
}

// 使用示例
struct ContentView: View {
    var body: some View {
        Color.white
            .onResizeEnded { size in
                print("View resizing ended. Final size: \(size)")
            }
            .padding()
    }
}

这样封装后,你只需要给任意视图调用.onResizeEnded方法,就能实现相同的功能,代码更整洁,也符合SwiftUI的组件化设计思想。

备注:内容来源于stack exchange,提问作者swiftPunk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 10:28:00