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

SwiftUI中Form嵌套下Sheet首次弹出后自动消失的问题求助

SwiftUI中Form嵌套下Sheet首次弹出后自动消失的问题求助

大家好,我遇到了一个奇怪的SwiftUI问题,想请各位帮忙看看。

我写了一个OutdoorSection组件,里面包含一个按钮,点击后会弹出LocationPicker的sheet来选择位置。但当这个组件被放在Form里面时,第一次点击“Select”按钮,sheet会弹出来然后立刻消失;第二次及之后点击,sheet就能正常保持打开状态。如果不放在Form里,整个功能就完全正常。

我已经做了两个预览(Form (buggy)和No Form)来复现这个问题,相关代码如下:

OutdoorSection 组件代码

import SwiftUI
import CoreLocation

struct OutdoorSection: View {
    @Binding var locationName: String?
    @Binding var temperature: Double
    @Binding var relativeHumidity: Double
    let onLocationChanged: (CLLocationCoordinate2D?) -> Void
    
    @State private var showPickerSheet: Bool = false
    
    var body: some View {
        Section(header: Label("Outdoor", systemImage: "cloud.sun.fill")) {
            VStack {
                HStack {
                    Label(locationName ?? "", systemImage: "mappin.and.ellipse")
                    Spacer()
                    Button("Select") {
                        showPickerSheet = true
                    }
                    .buttonStyle(.borderless)
                }
                TemperatureHumidityInputs(temp: $temperature, rh: $relativeHumidity)
            }
        }
        .sheet(isPresented: $showPickerSheet) {
            LocationPicker{ name, coordinate in
                showPickerSheet = false
                locationName = name
                onLocationChanged(coordinate)
            }
        }
    }
}

#Preview("Form (buggy)") {
    Form {
        OutdoorSection(
            locationName: .constant("Berlin"),
            temperature: .constant(20.0),
            relativeHumidity: .constant(50.0),
            onLocationChanged: { _ in }
        )
    }
}

#Preview("No Form") {
    OutdoorSection(
        locationName: .constant("Berlin"),
        temperature: .constant(20.0),
        relativeHumidity: .constant(50.0),
        onLocationChanged: { _ in }
    )
}

LocationPicker 组件代码

import SwiftUI
import MapKit
import CoreLocation

struct LocationPicker: View {
    @Environment(\.dismiss) private var dismiss
    @State var vm = LocationPickerViewModel()
    
    /// callback: (placeName, coordinate) or (nil, nil) on reset
    let onSelect: (String?, CLLocationCoordinate2D?) -> Void
    
    var body: some View {
        NavigationView {
            VStack {
                // Input + user-location button
                HStack {
                    TextField("Enter address…", text: $vm.query)
                        .textFieldStyle(.roundedBorder)
                    Button {
                        vm.requestCurrentLocation()
                    } label: {
                        Image(systemName: "location")
                            .font(.title2)
                    }
                    .disabled(vm.isRequestingLocation)
                    .help("Use current location")
                }
                .padding()
                
                // Show any location-error
                if let err = vm.locationError {
                    Text(err)
                        .foregroundColor(.red)
                        .font(.caption)
                }
                
                // Autocomplete list
                List(vm.suggestions, id: \.self) { suggestion in
                    VStack(alignment: .leading) {
                        Text(suggestion.title)
                            .font(.headline)
                        Text(suggestion.subtitle)
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                    }
                    .contentShape(Rectangle())
                    .onTapGesture {
                        vm.search(suggestion) { name, coord in
                            onSelect(name, coord)
                        }
                    }
                }
                .listStyle(.plain)
            }
            .navigationTitle("Pick a Location")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                // Close without callback
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Close") {
                        dismiss()
                    }
                }
                // Reset means “nil location”
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Reset") {
                        onSelect(nil, nil)
                    }
                }
            }
        }
    }
}

#Preview {
    ZStack{ }
        .sheet(isPresented: .constant(true)){
            LocationPicker { name, coord in
                print("Selected Location: \(name ?? "Unknown"), Coordinates: \(coord?.latitude ?? 0), \(coord?.longitude ?? 0)")
            }
        }
}

注:TemperatureHumidityInputs和LocationPickerViewModel的代码我没贴,因为它们不影响这个问题的复现。

我之前和GPT聊过,它建议把showPickerSheet这个状态变量移到OutdoorSection外面,避免重渲染时被重置。我试着用Binding来实现,但没成功。而且我觉得这个状态变量属于OutdoorSection本身,放在父组件里不太符合代码封装的思路。

有没有大佬知道这个问题的根源是什么,或者有没有更好的解决办法?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:07:58