Значение, передаваемое методом делегата, равно нулю.

#ios #swift #delegates #closures

Вопрос:

Я пытаюсь отправить данные с одного контроллера представления (ItenaryVC) через делегат другому контроллеру представления (ItenaryFloatingPanelVC). VC, который инициирует ItenaryVC, является DiscoverVC. Когда ItenaryVC загрузится, он выполнит вызовы API для получения некоторых данных, и я хочу передать данные, полученные от вызовов API, созданному мной делегату, который передаст данные в ItenaryFloatingPanelVC. Но данные не получены, и они равны нулю. Я также фактически использую библиотеку FloatingPanel для добавления панели в ItenaryVC.

Поток

DiscoverVC -> ItenaryVC ItenaryFloatingPanelVC (в качестве панели управления)

DiscoverVC.swift

 protocol DiscoverVCDelegate : AnyObject {
    func didSendSingleLocationData(_ discoverVC : DiscoverVC , location : Location)
}

class DiscoverVC : UIViewController {
    //MARK:- IBOutlets
    @IBOutlet weak var collectionView: UICollectionView!
    
    weak var delegate : DiscoverVCDelegate?
    
    private var locationResult = [Location]()
    private var selectedAtRow : Int!
    
    //MARK:- Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        renderView()
        getLocations()
    }
    
    private func renderView() {
        
        collectionView.register(UINib(nibName: R.nib.discoverCell.name, bundle: nil), forCellWithReuseIdentifier: R.reuseIdentifier.discoverCell.identifier)
        collectionView.delegate = self
        collectionView.dataSource = self
    }
    
    private func getLocations(location : String = "locations") {
        
        NetworkManager.shared.getLocations(for: location) {  [weak self] location in
            
            switch location {
            
            case .success(let locations):
                self?.updateDiscoverUI(with: locations)
                
            case .failure(let error):
                print(error.rawValue)
            }
        }
    }
    
    private func updateDiscoverUI(with locations : [Location]) {
        
        DispatchQueue.main.async { [weak self] in
            self?.locationResult.append(contentsOf: locations)
            self?.collectionView.reloadData()
        }
    }
}

//MARK:- Delegate
extension DiscoverVC : UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        selectedAtRow = indexPath.row
        
        self.performSegue(withIdentifier: R.segue.discoverVC.goToDetails, sender: self)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        guard let destinationVC = segue.destination as? ItenaryVC else { return}
        
        destinationVC.locationDetails = locationResult[selectedAtRow]
        destinationVC.imageURL = locationResult[selectedAtRow].image
        destinationVC.getItenaries(at: locationResult[selectedAtRow].itenaryName)
        
        delegate?.didSendSingleLocationData(self, location: locationResult[selectedAtRow])
        // Remove tab bar when push to other vc
        destinationVC.hidesBottomBarWhenPushed = true
        
    }
}
 

ИтенарыВК.свифт

 import UIKit
import FloatingPanel


protocol ItenaryVCDelegate : AnyObject {
    func didSendItenaryData(_ itenaryVC : ItenaryVC, with itenary : [[Days]])
}

class ItenaryVC: UIViewController {
    
    @IBOutlet weak var backgroundImage: UIImageView!

    var imageURL : URL!
    var locationDetails: Location! {
    didSet {
        getItenaries(at: locationDetails.itenaryName)
    }
}
    
    weak var delegate : ItenaryVCDelegate?
   
    var itenaries = [[Days]]()
   
    //MARK:- Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCard()
        setupView()
    }

    
    func getItenaries(at itenaries : String = "Melaka"){
        NetworkManager.shared.getItenaries(for: itenaries) { [weak self] itenary in
            switch itenary {
            
            case .success(let itenary):
               // print(itenary)
                DispatchQueue.main.async {
                    self?.itenaries.append(contentsOf: itenary)
                    self?.delegate?.didSendItenaryData(self! , with: itenary)
                }
               
                print(itenaries.count)
            case .failure(let error):
                print(error.rawValue)
            }
        }
    }
    
}

//MARK:- Private methods
extension ItenaryVC {
    
    private func setupView() {
        backgroundImage.downloaded(from: imageURL)
        backgroundImage.contentMode = .scaleAspectFill
    }
    
    private func setupCard() {
        guard let itenaryFlotingPanelVC = storyboard?.instantiateViewController(identifier: "itenaryPanel") as? ItenaryFloatingPanelVC else { return}
        
        let fpc = FloatingPanelController()
        fpc.set(contentViewController: itenaryFlotingPanelVC)
        fpc.addPanel(toParent: self)

    }
}
 

ItenaryFloatingPanelVC.swift

 import UIKit

class ItenaryFloatingPanelVC: UIViewController{
    
    
    //MARK:- Outlets
    @IBOutlet weak var sloganLabel: UILabel!
    @IBOutlet weak var locationLabel: UILabel!
    @IBOutlet weak var locDesc: UITextView!
    @IBOutlet weak var itenaryTableView: UITableView!
    
    @IBOutlet weak var locDescHC: NSLayoutConstraint!
    
    var itenaries = [[Days]]()
    
    
    let itenaryVC = ItenaryVC()
    let discoverVC = DiscoverVC()
    
    override func viewDidLoad() {
        
        discoverVC.delegate = self
        itenaryVC.delegate = self
        
        itenaryTableView.dataSource = self
        itenaryTableView.delegate = self
    
        locDescHC.constant = locDesc.contentSize.height
        itenaryTableView.register(UINib(nibName: R.nib.itenaryCell.name, bundle: nil), forCellReuseIdentifier: R.nib.itenaryCell.identifier)
    }
}



//MARK:- DiscoverVCDelegate
extension ItenaryFloatingPanelVC : DiscoverVCDelegate {
    func didSendSingleLocationData(_ discoverVC: DiscoverVC, location: Location) {
        print(location)
        locationLabel.text = location.locationName
    }
}

//MARK:- ItenaryVC Delegate
extension ItenaryFloatingPanelVC : ItenaryVCDelegate {
 
    func didSendItenaryData(_ itenaryVC: ItenaryVC, with itenary: [[Days]]) {
        DispatchQueue.main.async {
            print(itenary)
            self.itenaries.append(contentsOf: itenary)
            self.itenaryTableView.reloadData()
            print("itenary (self.itenaries.count)")
        }
    }
    
}
 

Изображение ItenaryVC и ItenaryFloatingPanel

Комментарии:

1. Где находится код, который представляет ItenaryVC ? Я не вижу в ItenaryFloatingPanelVC этом ничего такого (я мог бы быть слепым). Или это ItenaryVC контроллер представления контейнера?

2. на самом деле, у него есть другой VC, который представляет ItenaryVC, но запрос API будет сделан, когда ItenaryVC инициирует, ItenaryFloatingPanelVC на панели, которая будет в ItenaryVC, просто обновите код по вашей ссылке

3. В среде раскадровки FloatingPanelController() (и других вызовах инициализатора по умолчанию) никогда не будет возвращен экземпляр, который вы ожидаете. Вам нужна правильная ссылка на экземпляр в раскадровке .

4. Хм, что вы подразумеваете под правильной ссылкой?, я в этом не уверен.

5. О, я вижу, что ты сейчас делаешь. Итак ItenaryVC , представляет ItenaryFloatingPanelVC , и вы передаете данные «вперед»?

Ответ №1:

Ваш код не работает, потому что вы установили делегат только что созданного ItenaryVC экземпляра:

 let itenaryVC = ItenaryVC()

// ...

itenaryVC.delegate = self
 

itenaryVC вот не тот ВК , который представили self , тот ItenaryFloatingPanelVC . Это совершенно новый, который вы создали.

Вместо этого вы можете указать делегата, ItenaryVC когда собираетесь представить ItenaryFloatingPanelVC :

 private func setupCard() {
    guard let itenaryFlotingPanelVC = storyboard?.instantiateViewController(identifier: "itenaryPanel") as? ItenaryFloatingPanelVC else { return }
    
    let fpc = FloatingPanelController()

    // here!
    self.delegate = itenaryFlotingPanelVC

    fpc.set(contentViewController: itenaryFlotingPanelVC)
    fpc.addPanel(toParent: self)
}
 

self теперь ItenaryVC это и itenaryFlotingPanelVC есть тот ВК , который self представляет.

Ответ №2:

Я не вижу, чтобы ты звонил getItenaries() ItenaryVC . Пожалуйста, проверьте еще раз.

Комментарии:

1. Да, на самом деле я уже реализовывал это раньше, просто обновите исходный код