以编程方式快速传递数据的问题

Posted

技术标签:

【中文标题】以编程方式快速传递数据的问题【英文标题】:Issue With Passing Data Programmatically Swift 【发布时间】:2017-12-29 20:13:32 【问题描述】:

我无法以编程方式将数据从一个 UICollectionViewController 传递到另一个 UICollectionViewController。

目前我的设置如下:

    传递数据的 UICollectionViewController (RestaurantController)

    1a。一个 UICollectionViewCell (RestaurantCell)

    这个 UICollectionViewCell 里面有一个嵌套的 UICollectionViewController 和另一个自定义 UICollectionViewCell (RestaurantCollectionViewCell)

    正在接收数据的 UICollectionViewController (MenuController)

    2a。一个 UICollectionViewCell (MenuCell)

在我的RestaurantCell 内部,我正在从 JSON 加载数据并将其附加到一个名为餐馆的新数组:var restaurants = [RestaurantModel]()。但是,当我尝试使用var restaurant: RestaurantModel? 在我的MenuController 中加载餐厅名称或任何餐厅对象时,我得到的值为零。我有一种感觉,要么我的设置不正确,要么我在某个地方犯了一个愚蠢的错误。也许两者兼而有之。我在下面为每个班级粘贴了我的代码。

在 MenuController 内返回 nil 的值:

print("Restaurant Name:", restaurant?.name)

print("Restaurant Id:", restaurant?.id)

自定义委派是否会导致问题?

非常感谢您的帮助和建议!

在我的RestaurantController里面:

 import UIKit
 import FBSDKLoginKit

 class RestaurantController: UICollectionViewController, UICollectionViewDelegateFlowLayout, SWRevealViewControllerDelegate, UISearchBarDelegate, RestaurantDelegate 

var restaurantCell: RestaurantCell?

private let restaurantCellId = "restaurantCellId"

override func viewDidLoad() 
    super.viewDidLoad()

    collectionView?.backgroundColor = UIColor.qpizzaWhite()
    collectionView?.register(RestaurantCell.self, forCellWithReuseIdentifier: restaurantCellId)


    if self.revealViewController() != nil 
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "icon_menu_24dp").withRenderingMode(.alwaysOriginal), style: .plain, target: self.revealViewController(), action: #selector(SWRevealViewController.revealToggle(_:)))
        self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
    



// FIXME: issue with this...navigationcontroller is presenting, not pushing ontop of stack view
func didTapRestaurantCell(cell: RestaurantCell) 
    print("Did Tap Restaurant Cell - Restaurant Controller")

    let layout = UICollectionViewFlowLayout()
    let controller = MenuController(collectionViewLayout: layout)
    navigationController?.pushViewController(controller, animated: true)



override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: restaurantCellId, for: indexPath) as! RestaurantCell
    cell.delegate = self
    return cell


override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 1


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    return CGSize(width: view.frame.width, height: view.frame.height)


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat 
    return 1


在我的RestaurantCell里面:

protocol RestaurantDelegate 
    func didTapRestaurantCell(cell: RestaurantCell)



class RestaurantCell: BaseCell, UISearchBarDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout 

var delegate: RestaurantDelegate?
var restaurants = [RestaurantModel]()
var filteredRestaurants = [RestaurantModel]()

private let restaurantCollectionViewCell = "restaurantCollectionViewCell"
private let activityIndicator = UIActivityIndicatorView()

lazy var searchBar: UISearchBar = 
    let sb = UISearchBar()
    sb.placeholder = "Search Restaurant"
    sb.barTintColor = .white
    UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).backgroundColor = UIColor.qpizzaWhite()
    sb.delegate = self
    return sb
()

lazy var collectionView: UICollectionView = 
    let layout = UICollectionViewFlowLayout()
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    cv.backgroundColor = .white
    return cv
()

override func setupViews() 
    super.setupViews()
    collectionView.register(RestaurantCollectionViewCell.self, forCellWithReuseIdentifier: restaurantCollectionViewCell)
    collectionView.delegate = self
    collectionView.dataSource = self

    backgroundColor = UIColor.qpizzaRed()

    addSubview(searchBar)
    addSubview(collectionView)

    _ = searchBar.anchor(topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 4, leftConstant: 4, bottomConstant: 0, rightConstant: 4, widthConstant: 0, heightConstant: 50)

    _ = collectionView.anchor(searchBar.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)

     loadRestaurants()



func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) 
    print(searchText)

    filteredRestaurants = self.restaurants.filter( (restaruant: RestaurantModel) -> Bool in

        return restaruant.name?.lowercased().range(of: searchText.lowercased()) != nil
    )

    self.collectionView.reloadData()


// MARK - Helper Methods
func loadRestaurants() 

    showActivityIndicator()

    APIManager.shared.getRestaurants  (json) in
        if json != .null 
            //                print("Restaurant JSON:", json)
            self.restaurants = []

            if let restaurantList = json["restaurants"].array 
                for item in restaurantList 
                    let restaurant = RestaurantModel(json: item)
                    self.restaurants.append(restaurant)
                
                self.collectionView.reloadData()
                self.hideActivityIndicator()
            
         else 
            print("Error loading JSON into Restaurant ViewController")
        
    


func loadImage(imageView: UIImageView, urlString: String) 

    let imageUrl: URL = URL(string: urlString)!
    URLSession.shared.dataTask(with: imageUrl)  (data, response, error) in
        if let error = error 
            print("Error loading image for Restaurant Controller:", error.localizedDescription)
        
        guard let data = data, error == nil else  return 

        DispatchQueue.main.async(execute: 
            imageView.image = UIImage(data: data)
        )
        .resume()


func showActivityIndicator() 
    activityIndicator.frame = CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0)
    activityIndicator.center = center
    activityIndicator.hidesWhenStopped = true
    activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
    activityIndicator.color = UIColor.qpizzaGold()

    addSubview(activityIndicator)
    activityIndicator.startAnimating()


func hideActivityIndicator() 
    activityIndicator.stopAnimating()


//MARK: CollectionView Delegate & DataSource Methods
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: restaurantCollectionViewCell, for: indexPath) as! RestaurantCollectionViewCell
    let restaurant: RestaurantModel

    if searchBar.text != "" 
        restaurant = filteredRestaurants[indexPath.item]
     else 
        restaurant = restaurants[indexPath.item]
    

    cell.restaurantNameLabel.text = restaurant.name
    cell.restaurantAddressLabel.text = restaurant.address

    if let logoName = restaurant.logo 
        let url = "\(logoName)"
        loadImage(imageView: cell.restaurantLogoImageView, urlString: url)
    

    return cell



func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    if searchBar.text != "" 
        return self.filteredRestaurants.count
    

    return self.restaurants.count


//FIXME: Restaurant Name Navigation Title is still not be passed from RestaurantCell to MenuController
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    print("Did Select Item - Restaurant Cell")

    let layout = UICollectionViewFlowLayout()
    let controller = MenuController(collectionViewLayout: layout)
    controller.restaurant = self.restaurants[indexPath.item]

    print("Controller", controller.restaurant) // Optional(QpizzaDelivery.RestaurantModel)
    print("Restaurant:", self.restaurants) // [QpizzaDelivery.RestaurantModel, QpizzaDelivery.RestaurantModel, QpizzaDelivery.RestaurantModel]
    print("IndexPath:", self.restaurants[indexPath.item]) // QpizzaDelivery.RestaurantModel

    delegate?.didTapRestaurantCell(cell: self)


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    return CGSize(width: frame.width, height: 200)


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat 
    return 0.5



在我的MenuController里面:

import UIKit

class MenuController: UICollectionViewController, UICollectionViewDelegateFlowLayout, SWRevealViewControllerDelegate 

private let menuCellId = "menuCellId"

var restaurant: RestaurantModel?
var menuItems = [MenuItemsModel]()

override func viewDidLoad() 
    super.viewDidLoad()

    collectionView?.backgroundColor = UIColor.qpizzaWhite()
    collectionView?.register(MenuCell.self, forCellWithReuseIdentifier: menuCellId)
    collectionView?.alwaysBounceVertical = true

    if self.revealViewController() != nil 
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "menu2-black-32").withRenderingMode(.alwaysOriginal), style: .plain, target: self.revealViewController(), action: #selector(SWRevealViewController.revealToggle(_:)))
        self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
    

    print("Restaurant Name:", restaurant?.name) // returns nil
    if let restaurantName = restaurant?.name 
        self.navigationItem.title = restaurantName
    

    loadMenuItems()



func loadMenuItems() 
    print("Restaurant Id:", restaurant?.id) // returns nil
    if let restaurantId = restaurant?.id 
        print("RestaurantId:", restaurantId)
    


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: menuCellId, for: indexPath) as! MenuCell
    return cell


override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 3


override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    let layout = UICollectionViewFlowLayout()
    let controller = MenuDetailsController(collectionViewLayout: layout)
    navigationController?.pushViewController(controller, animated: true)


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    return CGSize(width: view.frame.width, height: 120)



在我的MenuCell里面:

import UIKit

class MenuCell: BaseCell 

let restaurantLabel: UILabel = 
    let label = UILabel()
    label.text = "Restaurant King"
    label.font = UIFont.boldSystemFont(ofSize: 16)
    label.textColor = .black
    label.numberOfLines = 0
    return label
()

let mealImageView: UIImageView = 
    let iv = UIImageView()
    iv.image = #imageLiteral(resourceName: "button_chicken").withRenderingMode(.alwaysOriginal)
    iv.contentMode = .scaleAspectFill
    iv.clipsToBounds = true
    return iv
()

let mealDetailsLabel: UILabel = 
    let label = UILabel()
    label.text = "Grass fed grass, American cheese, and friez"
    label.font = UIFont.boldSystemFont(ofSize: 12)
    label.textColor = UIColor.qpizzaBlack()
    label.numberOfLines = 0
    return label
()

let mealPriceLabel: UILabel = 
    let label = UILabel()
    label.text = "$12.00"
    label.font = UIFont.boldSystemFont(ofSize: 12)
    label.textColor = UIColor.qpizzaBlack()
    return label
()

let sepereatorView: UIView = 
    let view = UIView()
    view.backgroundColor = UIColor.lightGray
    return view
()


override func setupViews() 
    super.setupViews()

    backgroundColor = UIColor.qpizzaWhite()

    addSubview(restaurantLabel)
    addSubview(mealImageView)
    addSubview(mealDetailsLabel)
    addSubview(mealPriceLabel)
    addSubview(sepereatorView)

    _ = mealImageView.anchor(topAnchor, left: nil, bottom: nil, right: rightAnchor, topConstant: 14, leftConstant: 0, bottomConstant: 0, rightConstant: 12, widthConstant: 60, heightConstant: 60)
    _ = restaurantLabel.anchor(topAnchor, left: leftAnchor, bottom: nil, right: mealImageView.leftAnchor, topConstant: 14, leftConstant: 12, bottomConstant: 0, rightConstant: 10, widthConstant: 0, heightConstant: 20)
    _ = mealDetailsLabel.anchor(restaurantLabel.bottomAnchor, left: leftAnchor, bottom: nil, right: mealImageView.leftAnchor, topConstant: 12, leftConstant: 12, bottomConstant: 0, rightConstant: 10, widthConstant: 0, heightConstant: 30)
    _ = mealPriceLabel.anchor(mealDetailsLabel.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 10, leftConstant: 12, bottomConstant: 10, rightConstant: 10, widthConstant: 0, heightConstant: 20)
    _ = sepereatorView.anchor(nil, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, topConstant: 0, leftConstant: 20, bottomConstant: 4, rightConstant: 20, widthConstant: 0, heightConstant: 1)




【问题讨论】:

自从我弄乱了CollectionView以来已经有一段时间了,但是为什么didSelectItemAtIndexPath在单元格内而不是控制器内?您显然还创建了 2 个不同的 MenuController 实例,一个在单元格的 didSelectItemAtIndexPath 方法中,另一个在 RestaurantDelegate didTapRestaurantCell 方法中,它没有设置 restaurant 属性。在第一个中,您设置了餐厅,但该控制器实例只是停留了一段时间并且没有使用 - 它不是由导航控制器推动的。 didSelectItemAtIndexPath' is inside of RestaurantCell` 因为我在RestaurantCell 中有一个嵌套的 UICollectionViewController。您说我正在创建两个不同的MenuController 是正确的。我不知道如何在 RestaurantCell 中引用 MenuController 虽然没有创建新实例...您有什么提示吗? 我需要RestaurantController 中的第一个MenuController 实例将导航控制器推入堆栈,并需要RestaurantCellMenuController 的第二个实例以获取restaurant 的引用(其中引用我的餐厅模型)在我的MenuController @a2b123 为什么要将单元格传回控制器类?为什么不直接将数据传回并segue 数据呢?您的协议应该包含餐厅类型的数据(这是您的模型数组),当您点击一个单元格时,您可以获得该单元格的数据,并提供给协议,它将委托给控制器类,然后您的 segue 将接受它到另一个班级。 @a2b123 我的意思是说委托部分,您将单元格传回,只需将点击的单元格数据传回,当调用返回到控制器类时,您可以将数据发送到第二类。 【参考方案1】:

快速浏览一下,声明一个正确类型的变量是开始。但是您实际上必须执行赋值 (=) 才能将数据或类引用从一个类移动到下一个类。

func didTapRestaurantCell(cell: RestaurantCell) 
  print("Did Tap Restaurant Cell - Restaurant Controller")

  let layout = UICollectionViewFlowLayout()
  let controller = MenuController(collectionViewLayout: layout)
  navigationController?.pushViewController(controller, animated: true)

  // you need to set the restaurant attribute of your new 
  // controller
  let indexPath = indexPath(for: cell)
  controller.restaurant = self.restaurants[indexPath.item]

【讨论】:

我如何在我的RestaurantController 中访问restaurants。我已经在RestaurantCell 中声明了该变量。另外,我如何在我的RestaurantController 中访问indexPath.item? @ahalls。我尝试过多次使用该方法,但我只是不确定如何访问这些部分 这还不够,您必须将 Cell 映射到 restaurants 数组中的元素。 我看不到你一开始是如何填充单元格的? func indexPath(for: UICollectionViewCell) Returns the index path of the specified cell. 对不起,我没有关注你的所有代码......但缺少的是创建后 MenuController 的属性值的实际分配 (=)。声明正确类型的属性是必要的第一步,接下来是对类的实际实例进行赋值【参考方案2】:

感谢所有评论并帮助我解决这个问题的人。我能够通过更改协议并将控制器作为参数传递来解决我的问题。

RestaurantCell:

protocol RestaurantDelegate 
    func didTapRestaurantCell(cell: RestaurantCell, withMenuController: MenuController)



func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    print("Did Select Item - Restaurant Cell")

    let layout = UICollectionViewFlowLayout()
    let controller = MenuController(collectionViewLayout: layout)
    controller.restaurant = self.restaurants[indexPath.item]
    delegate?.didTapRestaurantCell(cell: self, withMenuController: controller)

RestaurantController:

func didTapRestaurantCell(cell: RestaurantCell, withMenuController controller: MenuController) 
    print("Did Tap Restaurant Cell - Restaurant Controller")
    navigationController?.pushViewController(controller, animated: true)

【讨论】:

以上是关于以编程方式快速传递数据的问题的主要内容,如果未能解决你的问题,请参考以下文章

反应路由器,以编程方式导航时传递数据?

Segue 和 Button 以编程方式快速

xcode/ios:以编程方式启动模态视图控制器时如何传递数据

如何将 GraphQL 查询中的数据传递到以编程方式生成的页面上?

如何以编程方式将数据从表视图控制器传递到另一个控制器? [复制]

如何以编程方式在视图控制器之间传递数据?我想将我的UITextfield数据传递给另一个视图控制器[重复]