#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. Да, на самом деле я уже реализовывал это раньше, просто обновите исходный код