You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 17:50:55