如何在单独的视图控制器中将图像作为变量传递给UIImageView?

时间:2019-01-05 21:53:48

标签: swift image variables uiimageview

我要提取一些JSON数据并将其显示在UITableView(iOS / iPhone 8)中。它显示每个值的图像,标题和说明。但是,我已经成功解决了这个问题,无法将图像拖到单独的View Controller中。

我的意思是,当第一个View Controller上的一个单元被点击时,另一个视图控制器将打开以仅显示该单元中的信息。

我已经能够通过全局变量和indexPath来访问标题和描述。但是,由于与字符串冲突,因此这不适用于图像。

我在下面列出了我已经成功完成的标题和描述字符串的内容,然后显示了我的主张(这当然是行不通的。)

如何获取已经加载并位于数组中的图像,使其像标题和描述一样可以访问,并在另一个View Controller中使用?

用于格式化和收集JSON值的代码:

if let jsonData = myJson as? [String : Any] { // Dictionary
    if let myResults = jsonData["articles"] as? [[String : Any]] {
        // dump(myResults)

        for value in myResults {
            if let myTitle = value["title"] as? String {
                // print(myTitle)
                myNews.displayTitle = myTitle
            }

            if let myDesc = value["description"] as? String {
                myNews.displayDesc = myDesc
            }

            if let mySrc = value["urlToImage"] as? String {
                // print(mySrc)
                myNews.src = mySrc
            }

            self.myTableViewDataSource.append(myNews)
            // dump(self.myTableViewDataSource)

            // GCD
            DispatchQueue.main.async {
                self.myTableView.reloadData()
            }

        }
    }
} 

我在课堂外拥有两个变量,以便全局使用它们:

var petsIndex = ""
var petsDesc = "" 

与UITableView及其单元格一起使用的代码:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     let myCell = tableView.dequeueReusableCell(withIdentifier: "reuseCell", for: indexPath)

     let myImageView = myCell.viewWithTag(2) as! UIImageView
     let myTitleLabel = myCell.viewWithTag(1) as! UILabel

     myTitleLabel.text = myTableViewDataSource[indexPath.row].displayTitle

    let myURL = myTableViewDataSource[indexPath.row].src
    loadImage(url: myURL, to: myImageView)

    return myCell
}  

我用于将JSON值发送到另一个View Controller的代码。我通过利用这些全局变量来实现这一点:

// If a cell is selected, view transitions into a different view controller.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    petsIndex = self.myTableViewDataSource[indexPath.row].displayTitle
    petsDesc = self.myTableViewDataSource[indexPath.row].displayDesc

    // Img needs to go here

     myIndex = indexPath.row
     performSegue(withIdentifier: "segue", sender: self)

 } 

这就是我正在尝试解决问题的方法。我将字符串转换为UIImage?UIImageView?并将其作为数据传递。我将变量保留为空,并将在数据可用时对其进行更改。单击该单元格时将发生这种情况。然后在第二个View Controller中,我将为UIImageView使用IBOutlet:

// Variable outside of the class
var petsImg: UIImageView? = nil

petsImg = self.myTableViewDataSource[indexPath.row].src 

我为此感到难过。我收到有关图像为字符串且需要转换的错误。转换后,变量总是返回为空或nil。

更新: 我只是尝试这样做。它有效并且不会引发任何错误。但是,我仍然得到nil

的值

petsImg = UIImage(named: self.myTableViewDataSource[indexPath.row].src)

3 个答案:

答案 0 :(得分:0)

快速而肮脏的解决方案

在您的second view controller中使用以下方法加载图像:

// With petsImg being the url to your image:
if let url = URL(string: petsImg), let imageData = Data(contentsOf: url) { 
    imagePost.image = UIImage(data: data)
}

正确的解决方案

您不应使用全局变量将数据从一个视图控制器传递到另一视图控制器(read why)。而是查看此问题的答案,以了解如何将数据(在您的情况下:myNews对象)从表视图传输到详细信息视图: Send data from TableView to DetailView Swift

如果这太抽象了,您可以看一下本教程。它涵盖了您要执行的操作:https://www.raywenderlich.com/265-uisplitviewcontroller-tutorial-getting-started

答案 1 :(得分:0)

执行segue时,您可以截获呼叫,以便准备显示的视图控制器。此时此控制器的视图尚未加载,因此您需要在PostViewController内创建属性。您可以为标题,说明和图像创建属性。

但是,将这些信息作为NewsInfo对象传递起来要容易得多,例如:

struct NewsInfo {
    let displayTitle: String
    let displayDesc: String
    let src: String

    var imageURL: URL { return URL(string: src)! }
}

还有一个自定义单元格类,该类以NewsInfo对象作为参数来填充出口。

class NewsInfoTableViewCell: UITableViewCell {        
    var newsInfo: NewsInfo? {
        didSet {
            updateOutlets()
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        newsInfo = nil
    }

    private func updateOutlets() {
        textLabel?.text = newsInfo?.displayTitle
        detailTextLabel?.text = newsInfo?.displayDesc
        // loadImage()
    }
}

Setup the custom table cell class并在出队后设置newsInfo属性。

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell", for: indexPath) as! NewsInfoTableViewCell
    cell.newsInfo = myTableViewDataSource[indexPath.row]
    return cell
}

选中该单元格后,您可以将其作为发送方来执行segue,而不必设置全局变量来填充PostViewController上的viewDidLoad

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let cell = tableView.cellForRow(at: indexPath) as? NewsInfoTableViewCell {
        performSegue(withIdentifier: "segue", sender: cell)
    }
}

如果通过将ctrl从原型单元拖到IB中的PostViewController,以使该单元成为segue的发送者,来替换现有的segue,则可以删除整个方法。

我们之所以这样,是因为我们将通过向选择传递触发选择的单元格的NewsInfo对象来拦截segue,以为其准备目的地视图控制器。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    switch (segue.destination) {
    case (let controller as PostViewController, let cell as NewsInfoTableViewCell):
        controller.newsInfo = cell.newsInfo
    default:
        break
    }
}

类似于我们将NewsInfo对象传递到单元格以填充出口的方式,您可以对PostViewController.做同样的事情

答案 2 :(得分:0)

看起来您的图片不是图片,而是图片的网址。您可以使用像nuke这样的库将图像加载到图像视图中:https://github.com/kean/Nuke

由于网络请求是异步调用的,因此在尝试配置UIImageView的那一点很难看到。但是一旦获得网络响应,您将执行以下两项操作之一:

  1. 如果尚未加载视图控制器(即,在完成网络响应后立即加载它),则可以在“准备续集”方法中配置UIImageView。
  2. 如果您的视图控制器已经加载(这很好),则需要在prepare segue方法中设置对该视图控制器的引用。然后,您可以在发出网络请求后配置视图控制器。由于系统(导航堆栈)已经强烈地支持VC,因此我对VC的引用比较薄弱。

PS:我建议您反序列化对对象的JSON响应。这将有助于我们理解您的代码。当您传递字典对象时,很难看到您的问题。我建议您使用以下之一: 1. Codable protocol 2. ObjectMapper 3. MapCodableKit(这是我个人使用的图书馆)

PPS:我假设您使用情节提要。