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

SwiftUI预览宏:如何为预览提供私有State属性lookAroundScene的值

SwiftUI预览中设置私有@State属性lookAroundScene的方案

问题说明

Xcode 15起SwiftUI引入预览宏,我基于MapKit做了个简易应用,但不知道怎么给视图里的私有@State属性lookAroundScene赋值,希望预览能正常显示对应的场景图。以下是我的代码:

struct LocationDetailsView: View {
    @Binding var mapSelection: MKMapItem?
    @Binding var show: Bool
    @State private var lookAroundScene: MKLookAroundScene?
    @Binding var getDirections: Bool
    
    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading) {
                    Text(mapSelection?.placemark.name ?? "")
                        .font(.title2)
                        .fontWeight(.semibold)
                    
                    Text(mapSelection?.placemark.title ?? "")
                        .font(.footnote)
                        .foregroundStyle(.gray)
                        .lineLimit(2)
                        .padding(.trailing)
                }
                        
                Spacer()
                
                Button {
                    show.toggle()
                    mapSelection = nil
                } label: {
                    Image(systemName: "xmark.circle.fill")
                        .resizable()
                        .frame(width: 24, height: 24)
                        .foregroundStyle(.gray, Color(.systemGray5))
                }
            }
            .padding(.init(top: 40, leading: 20, bottom: 0, trailing: 20))
            
            if let scene = lookAroundScene {
                LookAroundPreview(initialScene: scene)
                    .frame(height: 200)
                    .cornerRadius(12)
                    .padding()
            } else {
                ContentUnavailableView("No preview available", systemImage: "eye.slash")
            }
            
            HStack(spacing: 24) {
                Button {
                    if let mapSelection {
                        mapSelection.openInMaps()
                    }
                } label: {
                    Text("Open in Maps")
                        .font(.headline)
                        .foregroundColor(.white)
                        .frame(width: 170, height: 48)
                        .background(.green)
                        .cornerRadius(12)
                }
                
                Button {
                    getDirections = true
                    show = false
                } label: {
                    Text("Get Directions")
                        .font(.headline)
                        .foregroundColor(.white)
                        .frame(width: 170, height: 48)
                        .background(.blue)
                        .cornerRadius(12)
                }
            }
            .padding(.horizontal)
        }
        .onAppear {
            print("DEBUG: Did call on appear")
            fetchLookAroundPreview()
        }
        .onChange(of: mapSelection) { oldValue, newValue in
            print("DEBUG: Did call on change")
            fetchLookAroundPreview()
        }
        .padding()
    }
}

extension LocationDetailsView {
    func fetchLookAroundPreview() {
        if let mapSelection {
            lookAroundScene = nil
            Task {
                let request = MKLookAroundSceneRequest(mapItem: mapSelection)
                lookAroundScene = try? await request.scene
                print("DEBUG: lookAroundScene \(lookAroundScene)")
            }
        }
    }
}

#Preview {
    LocationDetailsView(
        mapSelection: .constant(MKMapItem.forCurrentLocation()),
        show: .constant(true) ,
        getDirections: .constant(false)
    )
}

解决建议

1. 添加预览专用初始化器

因为lookAroundScene是私有@State,外部无法直接修改,给视图添加一个仅在Debug模式下生效的初始化器,用于预览时传入场景值:

在LocationDetailsView结构体中添加:

#if DEBUG
init(mapSelection: Binding<MKMapItem?>, show: Binding<Bool>, getDirections: Binding<Bool>, previewScene: MKLookAroundScene?) {
    self._mapSelection = mapSelection
    self._show = show
    self._getDirections = getDirections
    self._lookAroundScene = State(initialValue: previewScene)
}
#endif

2. 优化预览代码

原预览用MKMapItem.forCurrentLocation()可能无法在预览环境获取有效位置,建议替换为固定测试地点,同时通过包装视图异步加载场景:

#Preview {
    struct PreviewContainer: View {
        @State private var previewScene: MKLookAroundScene?
        // 使用北京天安门坐标作为测试地点,确保能获取到LookAround场景
        private let testMapItem = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 39.9042, longitude: 116.4074)))
        
        var body: some View {
            if let scene = previewScene {
                LocationDetailsView(
                    mapSelection: .constant(testMapItem),
                    show: .constant(true),
                    getDirections: .constant(false),
                    previewScene: scene
                )
            } else {
                ProgressView()
                    .onAppear {
                        Task {
                            let request = MKLookAroundSceneRequest(mapItem: testMapItem)
                            previewScene = try? await request.scene
                        }
                    }
            }
        }
    }
    return PreviewContainer()
}

3. 临时调整权限(不推荐)

如果只是临时测试,可以暂时去掉lookAroundScene的private修饰符,直接在预览中通过$lookAroundScene绑定赋值,但测试完成后记得恢复私有权限,避免破坏封装性。

效果展示

根据上述方案修改后,预览可以正常显示场景图:
更新后的预览效果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 19:50:34