Swift中使用Delegate传递UIImage至ViewController遇阻求助
如何在Tabman标签页间通过Delegate传递UIImage?
更新
之前传递UIImage的方式有误,因为无法确定接收UIImage数据的ViewController实例。我没有使用协议,而是通过AppDelegate.swift共享UIImage数组,这种方式运行正常。
我是Swift新手,需要实现以下功能:
- 使用Tabman库,有两个标签页视图控制器:FirstTabViewController和SecondTabViewController
- FirstTabViewController上有一个UIImageView和两个UIButton
- SecondTabViewController上有一个UICollectionView用于展示图片
- 点击“Get an Image”按钮时,从The Dog API服务器下载图片并显示在UIImageView中
- 点击“Save an Image”按钮时,Delegate应被触发,但实际没生效——SecondTabViewController的collectionView看不到图片,控制台也没打印Delegate触发的测试信息,不知道怎么修复
- 我试过通过AppDelegate保存ViewController实例,也搜过指定Delegate对应ViewController实例的方法,有答案建议用segue/prepare(),但因为用了Tabman,内置Segue操作起来很复杂
- 想找无需创建新segue,通过Delegate传递UIImage数据的方法
相关代码
1. Delegate协议代码
import UIKit protocol ImageDelegate { func sendImage(with image: UIImage) }
2. TabManViewController代码
该视图控制器用于声明Tabman的标签页视图,并将视图控制器实例保存到AppDelegate的属性中
import Foundation import Tabman import Pageboy class TabManViewController: TabmanViewController { private var viewControllers: Array<UIViewController> = [] override func viewDidLoad() { super.viewDidLoad() let firstVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FirstTabVC") as! FirstTabViewController let secondVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondTabVC") as! SecondTabViewController viewControllers.append(firstVC) viewControllers.append(secondVC) let ad = UIApplication.shared.delegate as? AppDelegate ad?.firstVCInstance = firstVC ad?.secondVCInstance = secondVC self.dataSource = self // Create bar let bar = TMBar.ButtonBar() bar.layout.transitionStyle = .snap // Customize bar.indicator.weight = .custom(value: 1) bar.indicator.tintColor = .black // tap center bar.layout.alignment = .centerDistributed // tap 사이 간격 bar.layout.interButtonSpacing = 12 // tap 선택 / 미선택 bar.buttons.customize { (button) in button.tintColor = .gray button.selectedTintColor = .black button.selectedFont = UIFont.systemFont(ofSize: 16, weight: .medium) } // Add to view addBar(bar, dataSource: self, at: .top) } } //MARK: - Tabman DataSource extension TabManViewController: PageboyViewControllerDataSource, TMBarDataSource { func barItem(for bar: TMBar, at index: Int) -> TMBarItemable { let item = TMBarItem(title: "") if index == 0 { item.title = "Get a dog" } else if index == 1 { item.title = "Gallery" } else { item.title = "Page \(index+1)" } item.image = UIImage(named: "image.png") // ?? return item } func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int { return viewControllers.count } func viewController(for pageboyViewController: PageboyViewController, at index: PageboyViewController.PageIndex) -> UIViewController? { return viewControllers[index] } func defaultPage(for pageboyViewController: PageboyViewController) -> PageboyViewController.Page? { return nil } }
3. FirstTabViewController代码
import Tabman import UIKit import Alamofire class FirstTabViewController: TabmanViewController { var delegate: ImageDelegate? var dogImage: UIImage? @IBOutlet weak var imageView: UIImageView! @IBAction func GetImagePressed(_ sender: UIButton) { getADog() } @IBAction func SaveImagePressed(_ sender: UIButton) { print("==") print("SaveImage Btn Pressed") if let safeImage = dogImage { delegate?.sendImage(with: safeImage) } else { print("dogImage is nil") } } private var apiKey: String { get { guard let filepath = Bundle.main.path(forResource: "Keys", ofType: "plist") else { fatalError("Couldn't find file 'Keys.plist'.") } let plist = NSDictionary(contentsOfFile: filepath) guard let value = plist?.object(forKey: "API_KEY") as? String else { fatalError("Couldn't find key 'API_KEY' in 'Keys.plist'.") } return value } } //MARK: - View Life Cycle override func viewDidLoad() { super.viewDidLoad() } }
4. SecondTabViewController代码
import Foundation import Tabman class SecondTabViewController: TabmanViewController { @IBOutlet weak var collectionView: UICollectionView! var collectedDogImages = [UIImage]() let sectionInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) override func viewDidLoad() { collectionView.dataSource = self collectionView.delegate = self let ad = UIApplication.shared.delegate as? AppDelegate if let firstVCInstance = ad?.firstVCInstance { firstVCInstance.delegate = self } else { print("Error: Can't read firstVCInstance from Appdelegate.") } } } //MARK: - CollectionView extension SecondTabViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { print("cell count = \(collectedDogImages.count)") // It says always 0 return collectedDogImages.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? UICollectionViewCell else { return UICollectionViewCell() } let imageData = collectedDogImages[indexPath.row - 1] let appendingView = UIView(frame: CGRect.init()) let appendingImageView = UIImageView(image: imageData) appendingView.addSubview(appendingImageView) cell.contentView.addSubview(appendingView) return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = collectionView.frame.width let height = collectionView.frame.height let itemsPerRow: CGFloat = 2 let widthPadding = sectionInsets.left * (itemsPerRow + 1) let itemsPerColumn: CGFloat = 3 let heightPadding = sectionInsets.top * (itemsPerColumn + 1) let cellWidth = (width - widthPadding) / itemsPerRow let cellHeight = (height - heightPadding) / itemsPerColumn return CGSize(width: cellWidth, height: cellHeight) } } //MARK: - InsertImageDelegate extension SecondTabViewController: ImageDelegate { func sendImage(with image: UIImage) { //test print("=") print("secondTab delegate method activated") // Never printed on console self.collectedDogImages.append(image) let itemLocation = self.collectedDogImages.count let indexPaths = IndexPath(item: itemLocation, section: 0) self.collectionView.insertItems(at: [indexPaths]) } }
解决方案
问题根源
你的Delegate未生效核心有两个原因:
- 内容控制器继承错误:FirstTab和SecondTab继承了
TabmanViewController,但它们只是普通标签页内容,应该继承UIViewController,错误继承会导致实例管理和生命周期异常。 - Delegate赋值逻辑冗余且不可靠:通过AppDelegate中转实例容易出现引用问题,且在Tabman初始化时直接关联两个控制器更稳妥。
- CollectionView索引和视图布局错误:索引越界、ImageView未设置frame导致无法显示。
修复步骤
1. 修正内容控制器的继承类
将两个标签页控制器的父类从TabmanViewController改为UIViewController:
// FirstTabViewController.swift class FirstTabViewController: UIViewController { // 原有代码不变 } // SecondTabViewController.swift class SecondTabViewController: UIViewController { // 原有代码不变 }
2. 在TabmanViewController中直接设置Delegate
删除AppDelegate相关代码,直接在创建子控制器时完成Delegate绑定:
// TabManViewController.swift的viewDidLoad方法中 let firstVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FirstTabVC") as! FirstTabViewController let secondVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondTabVC") as! SecondTabViewController // 直接绑定Delegate firstVC.delegate = secondVC viewControllers.append(firstVC) viewControllers.append(secondVC) // 删除以下代码 // let ad = UIApplication.shared.delegate as? AppDelegate // ad?.firstVCInstance = firstVC // ad?.secondVCInstance = secondVC
3. 修复CollectionView的索引和视图问题
// SecondTabViewController的cellForItemAt方法 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? UICollectionViewCell else { return UICollectionViewCell() } // 修正索引,去掉-1 let imageData = collectedDogImages[indexPath.row] let appendingImageView = UIImageView(image: imageData) // 设置ImageView的frame和显示模式,确保能看到图片 appendingImageView.frame = cell.contentView.bounds appendingImageView.contentMode = .scaleAspectFill appendingImageView.clipsToBounds = true cell.contentView.addSubview(appendingImageView) return cell }
4. 修复InsertItems的索引错误
// SecondTabViewController的sendImage方法 func sendImage(with image: UIImage) { print("=") print("secondTab delegate method activated") self.collectedDogImages.append(image) // 数组索引从0开始,新增元素的位置是count-1 let itemLocation = self.collectedDogImages.count - 1 let indexPath = IndexPath(item: itemLocation, section: 0) self.collectionView.insertItems(at: [indexPath]) }
额外优化
补充getADog()方法的实现(确保图片下载后正确赋值):
// FirstTabViewController中添加 func getADog() { let headers: HTTPHeaders = ["x-api-key": apiKey] AF.request("https://api.thedogapi.com/v1/images/search", headers: headers).responseJSON { response in switch response.result { case .success(let value): if let jsonArray = value as? [[String: Any]], let imageUrlStr = jsonArray.first?["url"] as? String, let imageUrl = URL(string: imageUrlStr) { DispatchQueue.global().async { if let imageData = try? Data(contentsOf: imageUrl), let image = UIImage(data: imageData) { DispatchQueue.main.async { self.dogImage = image self.imageView.image = image } } } } case .failure(let error): print(error) } } }
内容的提问来源于stack exchange,提问作者apple.cat.4683797
相关产品推荐
相关产品推荐

