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

SwiftUI自定义拖拽底部视图:避免拖拽时调用content()方法

解决自定义底部弹窗拖拽时content()频繁调用的问题

问题根源

拖拽过程中DragGesture的updating闭包会实时更新dragOffset,触发整个视图的body重计算,而content()方法在body中被调用,导致每次重绘都会执行content(),出现频繁打印的情况。我们需要让内容视图仅在offsetViewState变化时才重建,而非每次拖拽都刷新。

解决方案

1. 用@ViewBuilder重构content方法,避免AnyView类型擦除

AnyView会丢失视图的类型信息,导致SwiftUI无法复用已有视图,每次都会重新创建。改用@ViewBuilder返回明确的视图类型,让SwiftUI能跟踪视图身份,仅在状态变化时切换视图:

@ViewBuilder
private func content(offsetViewState: OffsetViewState) -> some View {
    print("ONLY PRINTED WHEN offsetViewState CHANGES")
    switch offsetViewState {
    case .searchState, .zoneListState, .shortZoneListState, .default:
        viewModel.createSearchZonesView()
    case .zoneInfoState, .shortZoneInfoState:
        viewModel.createZoneDetailsView(baseFlowModel: baseFlowModel)
    }
}

2. 用EquatableView包装内容视图(可选)

如果createSearchZonesView和createZoneDetailsView返回的视图遵循Equatable,可以用EquatableView强制SwiftUI仅在视图数据变化时更新:

@ViewBuilder
private func content(offsetViewState: OffsetViewState) -> some View {
    switch offsetViewState {
    case .searchState, .zoneListState, .shortZoneListState, .default:
        EquatableView(content: viewModel.createSearchZonesView())
    case .zoneInfoState, .shortZoneInfoState:
        EquatableView(content: viewModel.createZoneDetailsView(baseFlowModel: baseFlowModel))
    }
}

注:需要确保视图的依赖数据(如viewModel中的相关状态)正确实现Equatable,否则无法生效。

3. 提取独立子视图,简化body复杂度

将不同状态的内容拆分为独立子视图,让SwiftUI更高效地管理视图生命周期,仅在子视图的依赖状态变化时更新:

// 定义独立子视图
private struct SearchZonesContentView: View {
    let viewModel: YourViewModelType
    var body: some View {
        viewModel.createSearchZonesView()
    }
}

private struct ZoneDetailsContentView: View {
    let viewModel: YourViewModelType
    let baseFlowModel: BaseFlowModelType
    var body: some View {
        viewModel.createZoneDetailsView(baseFlowModel: baseFlowModel)
    }
}

// 重构content方法
@ViewBuilder
private func content(offsetViewState: OffsetViewState) -> some View {
    switch offsetViewState {
    case .searchState, .zoneListState, .shortZoneListState, .default:
        SearchZonesContentView(viewModel: viewModel)
    case .zoneInfoState, .shortZoneInfoState:
        ZoneDetailsContentView(viewModel: viewModel, baseFlowModel: baseFlowModel)
    }
}

额外注意

确保dragOffset是@State变量,仅用于控制视图的偏移位置,不要让内容视图依赖这个变量。这样拖拽时只会更新视图的位置,不会触发内容视图的重建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:34:54