如何在 Swiftui 上将“ImagePickerView”类型的值转换为预期的参数类型“String”?

Posted

技术标签:

【中文标题】如何在 Swiftui 上将“ImagePickerView”类型的值转换为预期的参数类型“String”?【英文标题】:How to convert value of type 'ImagePickerView' to expected argument type 'String' on Swiftui? 【发布时间】:2021-06-03 17:28:16 【问题描述】:

我正在尝试构建一个应用程序,用户可以在其中插入电影的名称,并可以从照片库(使用 UIKit)直接将图像添加到应用程序中。幸运的是,用户可以从中插入文本和图像的部分照片库有效。我的问题是将数据从 .sheet 传输到列表。用户插入的 TextFields 中的信息工作正常并显示在列表中,但图像未显示。我不断收到错误消息“无法将 'ImagePickerView' 类型的值转换为预期的参数类型 'String'”。我不知道如何解决此问题。当我尝试插入图像时,此问题出现在 ContentView.swift 文件中的 MovieRow 结构中()。任何帮助将不胜感激。在此先感谢。 下面是我的 ContentView 文件。 d

//  ContentView.swift
//  MovieListEditttt
//

import SwiftUI

struct ContentView: View 

@State var movieAdd: [MovieAdd] = []

@State private var newMovieName: String = ""
@State private var showNewMovie = false

@State private var newMovieImage = UIImage()

var body: some View 
    ZStack 
        VStack 
            HStack 
                Text("Movies Watched Ratings")
                    .font(.system(size: 40, weight: .black, design: .rounded
                    ))
                
                Spacer()
                
                Button(action: 
                    self.showNewMovie = true
                ) 
                    Image(systemName: "plus.circle.fill")
                        .font(.largeTitle)
                        .foregroundColor(.yellow)
                
            
            
            List
                ForEach(movieAdd) movie in
                    movieRow(movieAdd: movie)
                
            
        
        
        if showNewMovie 
            BlankView(bGColor: .black)
                .opacity(0.5)
                .onTapGesture 
                    self.showNewMovie = false
                
            NewMovieView(isShow: $showNewMovie, addMovie: $movieAdd, newMovieName: newMovieName)
                .transition(.move(edge: .bottom))
                .animation(.interpolatingSpring(stiffness: 200.0, damping: 25.0, initialVelocity: 10.0))
        
    



struct ContentView_Previews: PreviewProvider 
static var previews: some View 
    ContentView()



struct movieRow: View 

@ObservedObject var movieAdd : MovieAdd

var body: some View 
        VStack 
            
            Image(movieAdd.movieImage)
                .resizable()
                .frame(width: 100, height: 100)
            
            Text(movieAdd.movieName)
        



struct BlankView: View 
var bGColor: Color

var body: some View 
    VStack 
        Spacer()
    
    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    .background(bGColor)
    .edgesIgnoringSafeArea(.all)


这是我的 MovieAdd.swift 文件,我在其中初始化将放入列表中的所有变量。

import Foundation

class MovieAdd: ObservableObject, Identifiable 
var id = UUID()
@Published var movieName = ""
@Published var isComplete : Bool = false
@Published var movieImage : ImagePickerView

init(movieName: String, isComplete: Bool = false, movieImage: ImagePickerView) 
    self.movieName = movieName
    self.isComplete = isComplete
    self.movieImage = movieImage


这是我的 NewMovieView.swift 文件,用户可以在其中将他们的电影信息插入到 TextField 中,并从他们的照片库中插入图像。这里也是我使用 UIKit 的地方。

import SwiftUI

struct NewMovieView: View 

@Binding var isShow: Bool
@Binding var addMovie: [MovieAdd]

@State var newMovieName: String = ""

@State var isShowingImagePicker = false
@State var imageInBlackBox = UIImage()

var body: some View 
    ScrollView 
    VStack 
        VStack (alignment: .leading) 
            HStack 
                Text("Add a New Movie")
                    .font(.system(.title, design: .rounded))
                    .bold()
            
            ZStack 
                VStack 
                HStack (alignment: .center)
                    
                    Spacer()
                    Image(uiImage: imageInBlackBox)
                        .resizable()
                        .scaledToFill()
                        .frame(width: 200, height: 200)
                        .border(Color.black, width: 3)
                        .clipped()
                    Spacer()
                
                
                VStack 
                    Spacer()
                    Button(action: 
                        self.isShowingImagePicker.toggle()
                    , label: 
                        Text("Select Image")
                            .font(.system(size: 15))
                    )
                    .sheet(isPresented: $isShowingImagePicker, content:  ImagePickerView(isPresented: $isShowingImagePicker, selectedImage: $imageInBlackBox))
                
                
            
            
            Group 
            TextField("Enter the movie name", text: $newMovieName)
            .padding()
            .background(Color(.systemGray6))
            
            
            
            Button(action: 
                if self.newMovieName.trimmingCharacters(in: .whitespaces) == "" 
                    return
                
                
                if self.isShowingImagePicker 
                    return 
                
                
                self.isShow = false
                self.addMovieTask(movieName: self.newMovieName, movieImage: ImagePickerView(isPresented: $isShowingImagePicker, selectedImage: $imageInBlackBox))
            ) 
                Text("Save")
                    .font(.system(.headline, design: .rounded))
                    .foregroundColor(.red)
            
        
    
        .background(Color.white)
    


private func addMovieTask(movieName: String, isComplete: Bool = false, movieImage: ImagePickerView) 
    
    let task = MovieAdd(movieName: movieName, movieImage: movieImage)
    addMovie.append(task)



struct NewMovieView_Previews: PreviewProvider 
static var previews: some View 
    NewMovieView(isShow: .constant(true), addMovie: .constant([]), newMovieName: "", isShowingImagePicker: true)



struct ImagePickerView: UIViewControllerRepresentable 

@Binding var isPresented: Bool
@Binding var selectedImage: UIImage

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePickerView>) -> some UIViewController 
    let controller = UIImagePickerController()
    controller.delegate = context.coordinator
    return controller


func makeCoordinator() -> ImagePickerView.Coordinator 
    return Coordinator(parent: self)


class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate 
    
    let parent: ImagePickerView
    init(parent: ImagePickerView)
        self.parent = parent
    
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
        if let selectedImage = info[.originalImage] as? UIImage 
            print(selectedImage)
            self.parent.selectedImage = selectedImage
        
        self.parent.isPresented = false
    


func updateUIViewController(_ uiViewController: ImagePickerView.UIViewControllerType, context: UIViewControllerRepresentableContext<ImagePickerView>) 
    //


【问题讨论】:

这里有这么多代码......将它归结为minimal reproducible example 真的很有帮助。我立即看到的是,您正在尝试使用图像选择器的字符串描述制作Image(因此是您的错误)。相反,您需要使用 Image(uiImage:) 从您的选择器中使用 selectedImage。但是,那里的代码太多了,我无法尝试挖掘并弄清楚实际意图是什么。 感谢您的帮助。更具体地说,当我尝试将它添加到我的 MovieAdd 数组时,我试图从 .sheet 获取图像以出现在 ContentView 文件中。提前致谢。 如果您可以将其缩减为重现它所需的代码,我很乐意看看。 @jnpdx 谢谢。我设法把它剪掉了一堆。由于 UIkit 代码,我无法修剪大部分 NewMovieView.swift 文件。但是另外两个文件,我设法修剪了一堆。此外,这些是我使用的仅有的三个文件。让我知道这是否有帮助。谢谢 【参考方案1】:

更改 #1:

你的模型应该通常struct,除非有一个真正令人信服的理由让它成为ObservableObject。在这种情况下,struct 效果很好:

struct MovieAdd: Identifiable 
    var id = UUID()
    var movieName = ""
    var isComplete : Bool = false
    var movieImage : UIImage

请注意,我已将 movieImage 设为 UIImage

更改 #2:

MovieRow 中使用Image(uiImage:)MovieAdd 属性不再需要 @ObservableObject,因为它只是一个 struct

还要注意 Swift 中的类型应该大写以遵循约定)。

struct MovieRow: View 
    
    var movieAdd : MovieAdd
    
    var body: some View 
        VStack 
            
            Image(uiImage: movieAdd.movieImage)
                .resizable()
                .frame(width: 100, height: 100)
            
            Text(movieAdd.movieName)
        
    


完整的代码以防我忘记提及任何其他更改:


struct ContentView: View 
    
    @State var movieAdd: [MovieAdd] = []
    
    @State private var newMovieName: String = ""
    @State private var showNewMovie = false
    
    @State private var newMovieImage = UIImage()
    
    var body: some View 
        ZStack 
            VStack 
                HStack 
                    Text("Movies Watched Ratings")
                        .font(.system(size: 40, weight: .black, design: .rounded
                        ))
                    
                    Spacer()
                    
                    Button(action: 
                        self.showNewMovie = true
                    ) 
                        Image(systemName: "plus.circle.fill")
                            .font(.largeTitle)
                            .foregroundColor(.yellow)
                    
                
                
                List
                    ForEach(movieAdd) movie in
                        MovieRow(movieAdd: movie)
                    
                
            
            
            if showNewMovie 
                BlankView(bGColor: .black)
                    .opacity(0.5)
                    .onTapGesture 
                        self.showNewMovie = false
                    
                NewMovieView(isShow: $showNewMovie, addMovie: $movieAdd, newMovieName: newMovieName)
                    .transition(.move(edge: .bottom))
                    .animation(.interpolatingSpring(stiffness: 200.0, damping: 25.0, initialVelocity: 10.0))
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    


struct MovieRow: View 
    
    var movieAdd : MovieAdd
    
    var body: some View 
        VStack 
            
            Image(uiImage: movieAdd.movieImage)
                .resizable()
                .frame(width: 100, height: 100)
            
            Text(movieAdd.movieName)
        
    


struct BlankView: View 
    var bGColor: Color
    
    var body: some View 
        VStack 
            Spacer()
        
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .background(bGColor)
        .edgesIgnoringSafeArea(.all)
    


struct MovieAdd: Identifiable 
    var id = UUID()
    var movieName = ""
    var isComplete : Bool = false
    var movieImage : UIImage



struct NewMovieView: View 
    
    @Binding var isShow: Bool
    @Binding var addMovie: [MovieAdd]
    
    @State var newMovieName: String = ""
    
    @State var isShowingImagePicker = false
    @State var imageInBlackBox = UIImage()
    
    var body: some View 
        ScrollView 
            VStack 
                VStack (alignment: .leading) 
                    HStack 
                        Text("Add a New Movie")
                            .font(.system(.title, design: .rounded))
                            .bold()
                    
                    ZStack 
                        VStack 
                            HStack (alignment: .center)
                                
                                Spacer()
                                Image(uiImage: imageInBlackBox)
                                    .resizable()
                                    .scaledToFill()
                                    .frame(width: 200, height: 200)
                                    .border(Color.black, width: 3)
                                    .clipped()
                                Spacer()
                            
                            
                            VStack 
                                Spacer()
                                Button(action: 
                                    self.isShowingImagePicker.toggle()
                                , label: 
                                    Text("Select Image")
                                        .font(.system(size: 15))
                                )
                                .sheet(isPresented: $isShowingImagePicker, content:  ImagePickerView(isPresented: $isShowingImagePicker, selectedImage: $imageInBlackBox))
                            
                        
                    
                    
                    Group 
                        TextField("Enter the movie name", text: $newMovieName)
                            .padding()
                            .background(Color(.systemGray6))
                        
                    
                    
                    Button(action: 
                        if self.newMovieName.trimmingCharacters(in: .whitespaces) == "" 
                            return
                        
                        
                        if self.isShowingImagePicker 
                            return
                        
                        
                        self.isShow = false
                        self.addMovieTask(movieName: self.newMovieName, movieImage: ImagePickerView(isPresented: $isShowingImagePicker, selectedImage: $imageInBlackBox))
                    ) 
                        Text("Save")
                            .font(.system(.headline, design: .rounded))
                            .foregroundColor(.red)
                    
                
            
            .background(Color.white)
        
    
    
    private func addMovieTask(movieName: String, isComplete: Bool = false, movieImage: ImagePickerView) 
        
        let task = MovieAdd(movieName: movieName, movieImage: movieImage.selectedImage)
        addMovie.append(task)
    


struct NewMovieView_Previews: PreviewProvider 
    static var previews: some View 
        NewMovieView(isShow: .constant(true), addMovie: .constant([]), newMovieName: "", isShowingImagePicker: true)
    


struct ImagePickerView: UIViewControllerRepresentable 
    
    @Binding var isPresented: Bool
    @Binding var selectedImage: UIImage
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePickerView>) -> some UIViewController 
        let controller = UIImagePickerController()
        controller.delegate = context.coordinator
        return controller
    
    
    func makeCoordinator() -> ImagePickerView.Coordinator 
        return Coordinator(parent: self)
    
    
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate 
        
        let parent: ImagePickerView
        init(parent: ImagePickerView)
            self.parent = parent
        
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
            if let selectedImage = info[.originalImage] as? UIImage 
                print(selectedImage)
                self.parent.selectedImage = selectedImage
            
            self.parent.isPresented = false
        
    
    
    func updateUIViewController(_ uiViewController: ImagePickerView.UIViewControllerType, context: UIViewControllerRepresentableContext<ImagePickerView>) 
        //
    

【讨论】:

嘿@jnpdx,我听从了你的建议,但是在 struct MovieAdd: Identifiable var id = UUID() var movieName = "" var isComplete : Bool = false var movieImage : UIImage 中,对于 var movieImage: UIImage 我收到一条错误消息“在范围内找不到类型 'UIImage'” 确保import UIKit

以上是关于如何在 Swiftui 上将“ImagePickerView”类型的值转换为预期的参数类型“String”?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Hstack SwiftUI 中将最后一个索引显示为默认值

SwiftUI误导性错误:'Int'不能转换为'CGFloat'

如何在macOS上将磁盘分为多个(非Apple)磁盘分区

如何在 UIView 上将 UILabel 居中

如何在 Android 上将 TextView 居中?

如何在 Swing 上将 ImageIcon 变为灰色