如何在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
相关产品推荐
相关产品推荐

