我有一个需要解析和显示的图片网址。 URL存在,但返回nil。
通过调用 cell.recipeImage.downloadImage(来自:(self.tableViewDataSource [indexPath.item] .image))成功解析cellForRowAt
函数
使用此行显示图像。但是,在didSelectRowAt
RecipeTableViewController.swift
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let resultsVC = Storyboard.instantiateViewController(withIdentifier: "ResultsViewController") as! ResultsViewController
// Information to be passed to ResultsViewController
if (tableViewDataSource[indexPath.item] as? Recipe) != nil {
if isSearching {
resultsVC.getTitle = filteredData[indexPath.row].title
//resultsVC.imageDisplay.downloadImage(from: (self.filteredData[indexPath.row].image))
} else {
resultsVC.getTitle = tableViewDataSource[indexPath.row].title
// Parse images
resultsVC.imageDisplay.downloadImage(from: (self.tableViewDataSource[indexPath.row].image))
}
}
// Push to next view
self.navigationController?.pushViewController(resultsVC, animated: true)
}
extension UIImageView {
func downloadImage(from url: String) {
let urlRequest = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.sync {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
ResultsViewController.swift
class ResultsViewController: UIViewController {
var getTitle = String()
var getImage = String()
@IBOutlet weak var recipeDisplay: UILabel!
@IBOutlet weak var imageDisplay: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
recipeDisplay.text! = getTitle
}
...
}
返回错误
线程1:致命错误:在解包可选值时意外发现nil
答案 0 :(得分:1)
根据我的理解,该应用程序正在崩溃:
recipeDisplay.text! = getTitle
如果是,显然这不是正确的方法。只需删除强制解包,因为默认情况下此处标签上的文本为nil。强制引用nil值会使应用程序崩溃。
recipeDisplay.text = getTitle
更新: - 让我们确保您正确连接标签和插座。将ti连接到VC,而不是文件所有者。
答案 1 :(得分:0)
您正在尚未初始化的视图上调用与视图相关的代码。请记住,IBOutlets是隐式展开的属性,因此如果您在初始化之前尝试访问它们,它们将强制解包并崩溃。所以并不是说UIImage的结果是零,这就是recipeDisplay是零并且正在解开力量。
惯用iOS要做的事情是将某种视图模型(对象或结构)交给视图控制器,然后让它在完成加载后对该项进行处理。
所以,在你的didSelect方法中,你可以创建你的视图模型(你需要定义)并将其交给它:
let title = filteredData[indexPath.row].title
let imageURL = self.tableViewDataSource[indexPath.row].image
let viewModel = ViewModel(title: title, imageURL: imageURL)
resultsVC.viewModel = viewModel
然后在你的resultsVC中,你会做这样的事情:
override func viewDidLoad() {
super.viewDidLoad()
if let vm = viewModel {
recipeDisplay.text = vm.title
downloadImage(from: vm.imageURL)
}
}
所以在你的情况下,你需要做的就是把这些字符串交给你的VC(你可以将它们包装在一个视图模型中或者单独交出)然后在那个VC的viewDidLoad()
那里你打电话给downloadImage(from:)
。这样,在加载子视图之前就没有调用子视图的危险。
最后一点说明:使用data
和error
变量及其对self的引用,您的下载方法应该更安全一些。请记住,每当您不必使用它时,请避免使用!
(请使用可选链接),除非您有充分理由不这样做,否则请始终在闭包中使用[weak self]
。
我建议这样做:
func downloadImage(from url: String) {
let urlRequest = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: urlRequest) { [weak self] (data,response,error) in
if let error = error {
print(error)
return
}
if let data = data {
DispatchQueue.main.sync {
self?.image = UIImage(data: data)
}
}
}
task.resume()
}
更新:因为'视图模型'概念有点太多了,让我解释一下。
视图模型只是一个对象或结构,表示屏幕需要处于可显示状态的表示数据。它不是Apple定义的类型的名称,并且未在iOS SDK中的任何位置定义。这是你需要自己定义的东西。因此,在这种情况下,我建议您在将要使用它的同时将其定义为相同的文件,即与ResultsViewController在同一文件中。
你会做这样的事情:
struct ResultsViewModel {
let title: String
let imageURL: String
}
然后在ResultsViewController上创建一个属性,如:
var viewModel: ResultsViewModel?
或者如果您不喜欢处理选项,您可以这样做:
var viewModel = ResultsViewModel(title: "", imageURL: "")
或者,您可以执行您已经在做的事情,但我高度建议重命名这些属性。 getTitle
听起来像是在做一些更多的东西,只是保持一个价值。 title
会是一个更好的名字。同样的批评是getImage
,另外批评它也有误导性,因为它听起来像存储图像,但事实并非如此。它存储图像网址。 imageURL
是一个更好的名字。