iOS开发:无需跳转页面,如何向其他ViewController传递数据?
NASA图片iOS应用数据传递问题
我正在开发一款从NASA API获取图片的iOS应用,基于UITabBarController,包含三个ViewController:
- HomeViewController:默认启动页,展示API返回的当日最新图片。点击页面按钮需将当前展示的图片数据传递给FavoritesViewController,但不需要跳转页面;目前只有跳转页面时数据传递生效,不跳转就传不了。
- BrowseViewController:UITabBar第二个页面,用CollectionView网格展示所有图片,功能正常。
- FavoritesViewController:UITabBar最后一个页面,是UITableView类型,需要展示图片的标题、拍摄日期和图片本身,但现在接收不到来自HomeViewController的图片数据。
我试过NotificationCenter、闭包(完成处理程序)、按钮点击时实例化目标ViewController并赋值这三种方法,都没成功。希望用非单例的简洁方案,而且应用没用到Storyboard。
相关代码
Photos 对象
import UIKit struct Photos: Codable { let date: String let explanation: String let title: String let url: URL? let media_type: String } extension Photos: Equatable { static func == (lhs: Photos, rhs: Photos) -> Bool { return lhs.url == rhs.url } } enum PhotoError: Error { case imageCreationError case missingImageURL }
HomeViewController
import UIKit import JGProgressHUD class HomeViewController: UIViewController { var photo: Photos! var photos = [Photos]() var store: PhotoStore! let scrollView: UIScrollView = { let scrollView = UIScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false return scrollView }() private let contentView: UIView = { let contentView = UIView() contentView.translatesAutoresizingMaskIntoConstraints = false return contentView }() var imageView: UIImageView = { let imageview = UIImageView() imageview.translatesAutoresizingMaskIntoConstraints = false imageview.contentMode = .scaleAspectFill imageview.layer.masksToBounds = true return imageview }() private var titleLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.numberOfLines = 0 label.font = .systemFont(ofSize: 24, weight: .semibold) label.backgroundColor = .tertiarySystemFill return label }() private var descriptionTextView: UITextView = { let textview = UITextView() textview.translatesAutoresizingMaskIntoConstraints = false textview.font = .systemFont(ofSize: 21) textview.textAlignment = .justified textview.textColor = .white textview.isEditable = false textview.backgroundColor = .black return textview }() var favsButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.setImage(UIImage(systemName: "heart"), for: .normal) return button }() var dateButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.setImage(UIImage(systemName: "star"), for: .normal) button.backgroundColor = .systemGreen return button }() override func loadView() { super.loadView() } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground navigationController?.navigationBar.prefersLargeTitles = true addSubviews() showHUD() fetchPhotoFromAPI() constraints() //MARK: - navigation buttons navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "calendar"), style: .plain, target: self, action: #selector(dateButtonTapped(_:))) navigationItem.leftBarButtonItem?.tintColor = .systemRed navigationItem.rightBarButtonItem?.tintColor = .systemRed } func fetchPhotoFromAPI() { store.fetchPhotosFromNasa {[weak self] photoResult in switch photoResult { case let .success(photos): self?.photos = photos if let lastPhoto = photos.last { print(lastPhoto.date) self?.updateImageView(for: lastPhoto) self?.photo = lastPhoto } case let .failure(error): print("error fetching interesting photos \(error)") } } } func updateImageView(for photo: Photos) { //first convert the string date to Date() object guard let convertedDate = DateFormatters.shared.inputDateFormatter.date(from: photo.date) else { return } let formattedDate = DateFormatters.shared.outPutDateFormatter.string(from: convertedDate) store.fetchImage(for: photo){[weak self] (imageResult) in switch imageResult { case let .success(image): self?.imageView.image = image self?.navigationItem.title = formattedDate self?.titleLabel.text = photo.title self?.descriptionTextView.text = photo.explanation case let .failure(error): print("Error downloading image: \(error)") } } } func showHUD() { let hud = JGProgressHUD() hud.textLabel.text = "Loading" hud.show(in: view) hud.dismiss(afterDelay: 3.2) } @objc func dateButtonTapped(_ sender: UIBarButtonItem) { //configure and present data picker } @objc func favsButtonTapped(_ sender: UIButton) { //The notifications approach // NotificationCenter.default.post(name: Notification.Name("photo"), object: photo) //also tried this approach let vc = FavoritesViewController() vc.store = self.store vc.photo = self.photo //but without presenting it like you normally would } } //MARK: - configure views and constraints extension HomeViewController { func constraints() { let heightConstraint = contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor) heightConstraint.priority = UILayoutPriority(250) NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), contentView.topAnchor.constraint(equalTo: scrollView.topAnchor), contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor), heightConstraint, // imageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), imageView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.3), titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -40), titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 10), titleLabel.heightAnchor.constraint(equalToConstant: 70), dateButton.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 5), dateButton.topAnchor.constraint(equalTo: titleLabel.topAnchor, constant: 10), dateButton.widthAnchor.constraint(equalToConstant: 35), dateButton.heightAnchor.constraint(equalToConstant: 45), descriptionTextView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), descriptionTextView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), descriptionTextView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10), descriptionTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), ]) } func addSubviews() { view.addSubview(scrollView) scrollView.addSubview(contentView) contentView.addSubview(imageView) contentView.addSubview(titleLabel) contentView.addSubview(descriptionTextView) contentView.addSubview(dateButton) dateButton.addTarget(self, action: #selector(favsButtonTapped(_:)), for: .touchUpInside) } }
FavoritesViewController
import UIKit import JGProgressHUD class FavoritesViewController: UIViewController, UITableViewDelegate { var observer: NSObjectProtocol? var store: PhotoStore! { didSet { tableDatSource.store = store } } var photo: Photos! { didSet { tableDatSource.photos.append(photo) tableDatSource.photo = photo } } var tableView: UITableView? let tableDatSource = TableDataSource() let tableview: UITableView = { let table = UITableView(frame: .zero, style: .grouped) // table.translatesAutoresizingMaskIntoConstraints = false table.register(CustomTableViewCell.self, forCellReuseIdentifier: CustomTableViewCell.identifier) return table }() //MARK: - lifecycle override func loadView() { super.loadView() setupTableView() tableview.reloadData() } override func viewDidLoad() { super.viewDidLoad() title = "Favorites" view.backgroundColor = .systemBackground observer = NotificationCenter.default.addObserver(forName: Notification.Name("photo"), object: nil, queue: .main, using: {[weak self] notification in guard let photo = notification.object as? Photos else{return} self?.photo = photo }) print("Received photo and it's \(photo)") } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } //MARK: - Table func setupTableView() { view.addSubview(tableview) tableview.dataSource = tableDatSource tableview.delegate = self tableview.translatesAutoresizingMaskIntoConstraints = false self.tableView = tableview } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() self.tableView?.frame = view.bounds } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 95.0 } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) } }
已尝试的无效方法
- 在HomeViewController发送NotificationCenter通知,FavoritesViewController添加观察者
- 使用闭包(完成处理程序)传递数据
- 在按钮点击事件中实例化FavoritesViewController并直接赋值photo属性
内容的提问来源于stack exchange,提问作者Agole
相关产品推荐
相关产品推荐

