SwiftUI 全屏 UIImagePickerController(相机)

Posted

技术标签:

【中文标题】SwiftUI 全屏 UIImagePickerController(相机)【英文标题】:SwiftUI Full-Screen UIImagePickerController (Camera) 【发布时间】:2020-04-22 21:54:49 【问题描述】:

我通过在sheet 修饰符中使用逻辑来在我的应用程序中呈现UIImagePickerController。简而言之,以下三种类型处理在 UIViewControllerRepresentable 类型内显示和关闭 UIImagePickerController 的实例,这可以按预期工作:

struct DetailsView: View 

    enum Sheet: Hashable, Identifiable 
        case takePhoto

        var id: Int  hashValue 
    

    @State private var activeSheet: Sheet?

    var body: some View 
        Text("Hello, World!")
            .sheet(item: $activeSheet)  (sheet) in self.view(for: sheet) 
    

    private func view(for sheet: Sheet) -> some View 
        switch sheet 
        case .takePhoto: return PhotoSelectionView(showImagePicker: .init(get:  sheet == .takePhoto , set:  (show) in self.activeSheet = show ? .takePhoto : nil ), image: $selectedImage, photoSource: .camera).edgesIgnoringSafeArea(.all)
        
    


struct ImagePicker: UIViewControllerRepresentable 

    @Binding var isShown: Bool

    @Binding var image: Image?

    let photoSource: PhotoSource

    func makeCoordinator() -> ImagePickerCoordinator 
        return ImagePickerCoordinator(isShown: $isShown, selectedImage: $image)
    

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) 

     

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController 
        let imagePicker = UIImagePickerController()
        if photoSource == .camera, UIImagePickerController.isSourceTypeAvailable(.camera) 
            imagePicker.sourceType = .camera
            imagePicker.cameraCaptureMode = .photo
        
        imagePicker.delegate = context.coordinator
        return imagePicker
    

class ImagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate 

    @Binding private var isShown: Bool

    @Binding private var selectedImage: Image?

    init(isShown: Binding<Bool>, selectedImage: Binding<Image?>) 
        _isShown = isShown
        _selectedImage = selectedImage
    

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
        // handle photo selection 
    

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) 
        dismiss()
    


我遇到的问题是相机视图是模态显示的,它没有覆盖整个屏幕。这会导致UIImagePickerController 有时在相机是源时出现布局损坏,就好像相机不是以这种方式呈现的一样。设置imagePicker.modalPresentationStyle = .fullScreen 不会导致全屏演示。

如何以全屏布局显示相机,使其不会出现在卡片式演示样式中?

【问题讨论】:

在非 SwiftUI 应用程序中,您可以通过设置所呈现视图的 modalPresentationStyle 来控制它。但这不适用于sheet。没有地方可以指定演示样式(并且尝试在UIImagePickerController 上设置它不起作用)。请参阅***.com/a/58970681/1271826 了解可能的解决方法。 只是一个友好的建议:将来如果您可以发布MCVE 可能会很有用。像PhotoSelectionViewPhotoSource 这样的东西在这里没有定义,这使得很难表现出问题,从而推断出发生了什么。如果您共享足够的代码来重现相关问题,您将来可能会收到更多回复... @Rob,您提供的 URL 很有帮助,因为我错过了 UIHostingController 可用于表示 SwiftUI 视图层次结构的概念。另外,我很欣赏创建 MCVE 的建议。我应该从我的原始代码示例中删除 PhotoSelectionViewPhotoSource 在 SwiftUI 中没有简单的方法来显示全屏 UIImagePicker 真是太疯狂了 【参考方案1】:

结合@LaX 和@GrandSteph 的答案,我有:

.fullScreenCover(isPresented: $activeSheet, content: 
    ImagePicker(image: $inputImage, sourceType: .camera)
       .edgesIgnoringSafeArea(.all)
 )

【讨论】:

【参考方案2】:

ios 14 中,Apple 添加了 fullScreenCover(ispresented:ondismiss:content:)fullScreenCover(item:ondismiss:content:) 方法,它们完全符合您的要求。

从你的例子:

    var body: some View 
        Text("Hello, World!")
            .fullScreenCover(item: $activeSheet)  (sheet) in self.view(for: sheet) 
    

或者,如果您只是想展示一个视图:

   @State var customViewIsShown = false

    // ...

    var body: some View 
        Text("Hello, World!")
        .fullScreenCover(isPresented: $customViewIsShown) 
            YourCustomView(isShown: $customViewIsShown)
        
    

【讨论】:

【参考方案3】:

好的,对这个答案持保留态度。因此,我可能会因为这个以及我所有的程序员孩子和我一起被投下地狱……但 Apple 也应该如此,因为它没有提供一种在 SwiftUI 中呈现全屏 UIImagePickerController 的简单方法。

非常糟糕的诀窍是在 rootView 中创建您的选择器并让它忽略安全区域。然后将所有必要的参数作为绑定传递给需要 imagePicker 的视图

struct mainView: View 

    @State private var imagePicked = UIImage()
    @State private var showImagePicker = false
    @State private var pickerSource = UIImagePickerController.SourceType.camera

    var body: some View 
        ZStack 
            AvatarView(showImagePicker: self.$showImagePicker, pickerSource: self.$pickerSource , imagePicked: self.$imagePicked)

            if self.showImagePicker 
                ImagePickerView(isPresented: self.$showImagePicker, selectedImage: self.$imagePicked, source: .camera).edgesIgnoringSafeArea(.all)
            
        
    

当然,您的 Avatar 视图将包含更新这些绑定所需的所有代码。像这样的

HStack () 
    Button(action: 
        self.showActionSheet.toggle()
    ) 
        MyImageView(image: self.imagePicked)
    
    .actionSheet(isPresented: $showActionSheet, content: 
        ActionSheet(title: Text("Picture source"), buttons: [
            .default(Text("Camera"), action: 
                self.pickerSource = .camera
                self.showImagePicker.toggle()
            ),
            .default(Text("Photo Library"), action: 
                self.pickerSource = .photoLibrary
                self.showImagePicker.toggle()
            ),
            .destructive(Text("Cancel"))
        ])
    )

老实说,我对提供这个解决方案犹豫不决,这段代码让我眼花缭乱,但我想有人在阅读这篇文章时可能会有更好的主意,并提供一种真正干净的 SwiftUI 方法。

这个解决方案的好处是它可以很好地管理方向的变化,让我们面对现实吧,UIImagePickerController 不适合工作表。照片库可以,相机不行。

【讨论】:

以上是关于SwiftUI 全屏 UIImagePickerController(相机)的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 全屏图片背景

如何全屏显示 SwiftUI 子视图?

如何在 SwiftUI 中呈现全屏 AVPlayerViewController

呈现 Modal 全屏 SwiftUI

SwiftUI 全屏 UIImagePickerController(相机)

SwiftUI 背景图片全屏