I have two viewController's. The first VC has collectionCell inside tableCell. The second VC has collectionCell, and I have swift File with Model have a class. I need to pass data from the first VC (collectionCell) to the second VC.
Pass data need from collectionCell because collectionCell has images (from swift File - Model). I think need use a protocol, but I don't really understand how to work with a protocol. Please help.
My code:
1stVC(viewController):
class ViewController1: UIViewController {
#IBOutlet weak var tableView: UITableView!
var image: [Model] = []
var ref: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference(withPath: "Студии2")
ref.observe(.value, with: { (snapshot) in
var newImages: [Model] = []
for item in snapshot.children {
let object = Model(snapshot: item as! FIRDataSnapshot)
newImages.append(object)
}
self.image = newImages
self.tableView.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController1: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return image.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell1
cell.images = [image[indexPath.row].image,
image[indexPath.row].image2]
return cell
}
}
1stVC(collectionCell inside tableCell):
protocol PostDelegate {
func selectedPost(cell: String)
}
class TableViewCell1: UITableViewCell {
var images = [String]()
var selecetdItem: Model?
var postDelegate: PostDelegate?
}
extension TableViewCell1: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell1
cell.imagesView.sd_setImage(with: URL(string: images[indexPath.item]))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
postDelegate?.selectedPost(cell: self.images[indexPath.item])
print(images[indexPath.item])
}
}
2ndVC(viewController):
class ViewController3: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
// var images = [String]()
var images: [Model] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController3: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell3
// let array = [images[indexPath.item].images,
// images[indexPath.item].images2]
// cell.imagesView.sd_setImage(with: URL(string: array[indexPath.item]))
// cell.imagesView.sd_setImage(with: URL(string: images[indexPath.item]))
return cell
}
}
Model(swift File have a class):
class Model {
var image: String!
var image2: String!
var images: [String] = []
var images2: [String] = []
var ref: FIRDatabaseReference!
init(snapshot: FIRDataSnapshot) {
ref = snapshot.ref
let value = snapshot.value as! NSDictionary
let snap1 = value["hall1"] as? NSDictionary
let snap2 = value["hall2"] as? NSDictionary
image = snap1?["qwerty"] as? String ?? ""
image2 = snap2?["qwerty"] as? String ?? ""
if let post1 = snap1 as? [String: AnyObject] {
for (_, value) in post1["images"] as! [String: AnyObject] {
self.images.append(value as! String)
}
}
if let post1 = snap1 as? [String: AnyObject] {
for (_, value) in post1["images"] as! [String: AnyObject] {
self.images2.append(value as! String)
}
}
}
}
Pass ViewController1 as the postDelegate of TableViewCell1
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell1
//make VC1 as post delegate of cell
(cell as! TableViewCell1).postDelegate = self
cell.images = [image[indexPath.row].image,
image[indexPath.row].image2]
return cell
}
Finally implement
extension ViewController1 : PostDelegate {
func selectedPost(cell: String) {
//call self.perform segue or load VC2 and pass data here
}
}
Pinch of Advice :
var postDelegate: PostDelegate?
will result in strongly holding a reference to the delegate being passed. Which will result in memory leaks. To make it safer declare delegate as weak
weak var postDelegate: PostDelegate?
In order to make the Protocol weak your protocol
protocol PostDelegate: NSObjectProtocol {
func selectedPost(cell: String)
}
Related
I have this two structs, the data I am getting from a Firebase Database and I don't know how to get each tableview cell to display a different collection view, I have both cells (TableviewCell and CollectionViewCell) in xib Files but can't get them the way I want it to.
I have attached my ViewController where they are both in and the TableViewCell class. Please help
struct Clothes {
var price: Double
var imgClUrl: String
var id: String
var isActive: Bool = true
var timeStamp: Timestamp
var stock: Double
var name: String
init(data : [String : Any]) {
self.price = data["price"] as? Double ?? 0
self.imgClUrl = data["imgClUrl"] as? String ?? ""
self.id = data["id"] as? String ?? ""
self.isActive = data["isActive"] as? Bool ?? true
self.timeStamp = data["timeStamp"] as? Timestamp ?? Timestamp()
self.stock = data["stock"] as? Double ?? 0
self.name = data["name"] as? String ?? ""
}
}
struct TypesOfCLothing {
var title : String
var id : String
var clothes : [Clothes]
init (data : [String : Any]){
self.title = data["title"] as? String ?? ""
self.id = data["id"] as? String ?? ""
self.clothes = data["Clothes"] as! [Clothes]
}
}
class eachStoreTableViewCell: UITableViewCell{
#IBOutlet weak var eachRowCollView: UICollectionView!
#IBOutlet weak var eachRowLbl: UILabel!
var clothes = [Clothes]()
override func awakeFromNib() {
super.awakeFromNib()
eachRowCollView?.reloadData()
eachRowCollView?.delegate = self
eachRowCollView?.dataSource = self
self.eachRowCollView?.register(UINib(nibName: "RowColectionView", bundle: nil), forCellWithReuseIdentifier: Identifiers.ClothesCell)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configureCell(clothingType: TypesOfCLothing) {
eachRowLbl?.text = clothingType.title}}
extension eachStoreTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clothes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Identifiers.ClothesCell, for: indexPath) as? RowColectionView{
cell.configureCell(clothes: clothes[indexPath.item])
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 160, height: 180)
}
class EachStoreVC: UIViewController {
#IBOutlet weak var StoreTblView: UITableView!
var typesOfClothing = [TypesOfCLothing]()
var tienda: Tiendas!
let db = Firestore.firestore()
var listener : ListenerRegistration!
override func viewDidLoad() {
super.viewDidLoad()
// StoreTblView?.delegate = self
// StoreTblView?.dataSource = self
StoreTblView?.register(UINib(nibName: "eachStoreTblViewCell", bundle: nil), forCellReuseIdentifier: Identifiers.ClothingTypeCell)
StoreTblView?.cellLayoutMarginsFollowReadableWidth = false
}
override func viewDidAppear(_ animated: Bool) {
}
override func viewDidDisappear(_ animated: Bool) {
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}}
extension EachStoreVC : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return typesOfClothing.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: Identifiers.ClothingTypeCell, for: indexPath) as? eachStoreTableViewCell {
cell.configureCell(clothingType: typesOfClothing[indexPath.row])
cell.frame = CGRect(x: 0, y: 0, width: StoreTblView.frame.size.width, height: cell.frame.size.height)
return cell
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}}
EDIT
The collectionView and Tableview now show fine but I'm having trouble getting the data from firebase, I have an array of document references inside each TypesOfClothing document, the code is as follows. I have also updated my 2 Structs.
func getRowData(){
listener = db.collection("TypesOfClothing").addSnapshotListener({ (snap, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
snap?.documentChanges.forEach({ (change) in
let data = change.document.data()
let typesOfClothing = TypesOfCLothing.init(data: data)
switch change.type {
case.added:
self.onDocumentAdded(change: change, category: typesOfClothing)
case.modified:
self.onDocumentModified(change: change, category: typesOfClothing)
case.removed:
self.onDocumentRemoved(change: change)
}
})
}) }
func onDocumentAdded(change: DocumentChange, category: TypesOfCLothing){
let newIndex = Int(change.newIndex)
typesOfClothing.insert(category, at: newIndex)
StoreTblView?.insertRows(at: [IndexPath(row: newIndex, section: 0)], with: UITableView.RowAnimation.left)
}
func onDocumentModified(change: DocumentChange, category: TypesOfCLothing){
if change.newIndex == change.oldIndex {
let index = Int(change.newIndex)
typesOfClothing[index] = category
StoreTblView?.reloadRows(at: [IndexPath(row: index, section: 0)], with: UITableView.RowAnimation.left)
} else {
let oldIndex = Int(change.oldIndex)
let newIndex = Int(change.newIndex)
typesOfClothing.remove(at: oldIndex)
typesOfClothing.insert(category, at: newIndex)
StoreTblView?.moveRow(at: IndexPath(row: oldIndex, section: 0), to: IndexPath(row: newIndex, section: 0))
}
}
func onDocumentRemoved(change: DocumentChange){
let oldIndex = Int(change.oldIndex)
typesOfClothing.remove(at: Int(oldIndex))
StoreTblView?.deleteRows(at: [IndexPath(row: oldIndex, section: 0)], with: UITableView.RowAnimation.fade)
}
Struct use like this:
struct TypesOfClothing {
var title : String
var clothes: [Clothes]
}
struct Clothes {
var price: Double
var imgClUrl: String
var id: String
var isActive: Bool = true
var timeStamp: Timestamp
var stock: Double
var name: String
}
in Main View controller: try code like this:
var typesOfClothing: [TypesOfClothing] = []
in numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return typesOfClothing.count
}
in cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: Identifiers.ClothingTypeCell, for: indexPath) as? eachStoreTableViewCell {
if indexPath.row < typesOfClothing?.count ?? 0 {
let cellData = typesOfClothing?[indexPath.row]
cell.configureCell(cellData)
}
return cell
}
return UITableViewCell()
}
in UITableViewCell class try like this:
class eachStoreTableViewCell: UITableViewCell{
#IBOutlet weak var eachRowCollView: UICollectionView!
#IBOutlet weak var eachRowLbl: UILabel!
var clothes = [Clothes]()
override func awakeFromNib() {
super.awakeFromNib()
eachRowCollView?.delegate = self
eachRowCollView?.dataSource = self
self.eachRowCollView?.register(UINib(nibName: "RowColectionView", bundle: nil), forCellWithReuseIdentifier: Identifiers.ClothesCell)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configureCell(_ clothingType: TypesOfClothing) {
eachRowLbl?.text = clothingType.title
clothes = clothingType.clothes
eachRowCollView?.reloadData()
}
}
extension eachStoreTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clothes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Identifiers.ClothesCell, for: indexPath) as? RowColectionView{
cell.configureCell(clothes: clothes[indexPath.item])
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 160, height: 180)
}
}
it'll help you.
You can make one structs for load tableView Data
struct ClothingData
{
var typesOfClothing : String = ""
var clothes : [Clothes] = []
}
Load this structArray for tabelView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return ClothingData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: Identifiers.ClothingTypeCell, for: indexPath) as? eachStoreTableViewCell
let row = ClothingData[indexPath.row]
cell.eachRowLbl?.text = row.typesOfClothing
cell.clothes = row.clothes
// Here you can pass clothesList to tabelViewCell for load collectionView
return cell
}
I have a ViewController class embedded with tableView in which I created two cells
First:
class CategoryTableViewCell: UITableViewCell {
//MARK:- IBOUTLETS
//MARK:-
#IBOutlet weak var collectionView: UICollectionView!
var categoryArray: [PopularCategories]! {
didSet {
self.collectionView.reloadData()
}
}
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: true)
}
}
In which I created I created a CollectionViewCell.
And in my 2nd TableViewCell class I reloaded the data which is coming from the api.
This is collectionView code inside TableViewCell class
extension CategoryTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categoryArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "CatergoriesCollectionViewCell", for: indexPath) as? CatergoriesCollectionViewCell else {
return UICollectionViewCell()
}
cell.nameLabel.text = categoryArray[indexPath.item].name
cell.image.sd_setImage(with: URL(string: categoryArray[indexPath.item].image ), placeholderImage: UIImage(named: "placeholderSmall"))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "CatergoriesCollectionViewCell", for: indexPath) as! CatergoriesCollectionViewCell
collectionCellTapHandler?()
let id = categoryArray[indexPath.item].id
self.categroyID = id
controller.categoryId = id
controller.filterDataUsingMostPopularCategory(id: id, lat: Latitude, long: Longitude)
print("Here I can access my view controller....\(controller.categoryId)")
print(cell.nameLabel.text!, id)
}
}
}
Now what I want I need to call a function which is in my ViewController when select a collectionView cell item. This the function in my ViewController class file I want to access when collectionViewCell is selected
class OneStopShopVC: TruckerConveyBaseVC {
func searchDataFromFilteredApi() {
let param: [String : Any] = ["lat": self.latitude, "lng": self.longitude, "title": selectedTitle, "category": "\(selectedCategory)"]
print(param)
CommonUtils.showHudWithNoInteraction(show: true)
Alamofire.request(Constants.BASE_URL+"search_home_ads.php", method: .post, parameters: param, encoding: URLEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
CommonUtils.showHudWithNoInteraction(show: false)
switch(response.result) {
case .success(_):
if let json = response.result.value as? [String:Any] {
print(json)
if let ads_list = json["ads_list"] as? [[String:Any]] {
self.adsListModel.removeAll()
let response = kSharedInstance.getArray(withDictionary: ads_list)
print(response)
self.adsListModel = response.map{ AdsListModel(with: $0) }
}
DispatchQueue.main.async {
self.reloadList()
}
}
break
case .failure(_):
print("Error")
break
}
}
}
}
Here is code inside UITableViewDataSource and Delegate
extension OneStopShopVC : UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return Int.getInt(self.adsListModel.count)
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return 181
} else {
return 121
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cellConfig(indexPath)
}
private func cellConfig(_ indexpath : IndexPath) -> UITableViewCell {
if indexpath.section == 0 {
guard let cell = oneStopShopTableView.dequeueReusableCell(withIdentifier: CategoryTableViewCell.cellIdentifier()) as? CategoryTableViewCell else {
return UITableViewCell()
}
cell.categoryArray = popularCategories
cell.collectionCellTapHandler = {[weak self] in
self?.filterDataUsingMostPopularCategory(id: cell.categroyID, lat: Latitude, long: Longitude)
}
cell.collectionView.reloadData()
return cell
}
else {
let cell = oneStopShopTableView.dequeueReusableCell(withIdentifier: OneStopShopTableCell.cellIdentifier()) as! OneStopShopTableCell
cell.lblPostTitle.text = String.getString(self.adsListModel[indexpath.row].post_title)
cell.lblPostedDate.text = String.getString(self.adsListModel[indexpath.row].posted_date)
cell.lblPostedExpDate.text = String.getString(self.adsListModel[indexpath.row].posted_expired_date)
cell.lblPostedDesc.text = String.getString(self.adsListModel[indexpath.row].post_desc)
cell.postedImage.sd_setImage(with: URL(string: adsListModel[indexpath.row].post_image ?? ""), placeholderImage: UIImage(named: ""))
let status = String.getString(self.adsListModel[indexpath.row].status)
if (status == "Publish") {
cell.statusLabel.text = "Published"
cell.statusLabel.textColor = #colorLiteral(red: 0.2745098174, green: 0.4862745106, blue: 0.1411764771, alpha: 1)
}
else if(status == "Banned") {
cell.statusLabel.textColor = UIColor.darkGray
}
else {
cell.statusLabel.textColor = UIColor.red
}
cell.priceLabel.text = "$\(String.getString(self.adsListModel[indexpath.row].price))"
return cell
}
}
Conclusion: When I click on CollectionViewCell item in first TableViewCell class I want to reload the data of SecondTableViewCell.. For that I need to access ViewController function to reload data. How can I do this?
In general, you have multiple options on how to solve this, you need to choose one of these based on different criteria.
The first option is as the answer before creating a closure function and assigns it to the cell from the viewController.
The second option is to implement a delegate pattern like this:
protocol MyDelegate:class {
func doTheJob()
}
class CategoryTableViewCell: UITableViewCell, UICollectionViewDelegate {
//rest of the code...
weak var myDelegate:MyDelegate? = nil
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
myDelegate?.doTheJob()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CategoryTableViewCell
cell.myDelegate = self
}
extension OneStopShopVC: MyDelegate {
func doTheJob() {
}
}
The third option can be to have one class which will in charge of such logic some kind of manager class. This class can be a singleton and you can instantiate from where you need it.
In general, you have a lot of solutions for this. But you need to think what is your need and to separate the code in the best way. Think about MVC, MVVM, VIPER or whatever you follow what are the basic principles of separations.
P.S you using an instance of UITableViewCell which is a view, as a ViewController this should turn big red flag for you that your architecture is not okay.
Use closure to handle this.
Create a closure named collectionCellTapHandler in CategoryTableViewCell and call it from collectionView(_: didSelectItemAt:) method.
class CategoryTableViewCell: UITableViewCell, UICollectionViewDelegate {
//rest of the code...
var collectionCellTapHandler: (()->())?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionCellTapHandler?()
}
}
In the above code, I've used a closure accepting 0 arguments. You can modify that as per your requirement.
Now set the collectionCellTapHandler in the ViewController in UITableViewDataSource's tableView(_: cellForRowAt:) method and call your custom method callTheMethod() from it.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CategoryTableViewCell
cell.collectionCellTapHandler = {[weak self] in
self?.callTheMethod()
}
return cell
}
func callTheMethod() {
print("Hello...")
}
Another way of using protocol design pattern, define a CategoryCollectionViewDelegate protocol
protocol CategoryCollectionViewDelegate {
/// You can define parameters as per your need.
func didSelectCategory(_ index: Int)
}
Now in CategoryTableViewCell
extension CategoryTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
var delegate_collection: CategoryCollectionViewDelegate?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.delegate_collection. didSelectCategory(indexPath.item)
}
}
Now in the ViewController in UITableViewDataSource's tableView(_: cellForRowAt:) method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CategoryTableViewCell
cell.delegate_collection = self
return cell
}
func didSelectCategory(_ index: Int){
print("array item index \(index)")
}
Try this.
You can pass the viewcontroller in cellforrow in your tableview
let cell = UITableViewCell()
cell.parentVC = self
return cell
then in you tableviewcell while loading collectionview you can similarly pass viewcontroller
cell.parentVC = parentVC
This works as i have implemented similar thing in my project.
you can use NotificationCenter for this kind of flow. A notification dispatch mechanism that enables the broadcast of information to registered observers.
Click the link for reference.
I wish to pass data through segue from a collectionViewCell embedded in a TableViewCell;
Also from a Button embedded in a TableViewCell
Here is the sample code :
Here is the TableViewCell's class :
class PopularCell: UITableViewCell {
#IBOutlet weak var ViewAllButton: UIButton!
#IBOutlet weak var PopularEvents: UILabel!
#IBOutlet weak var EventCollection: UICollectionView! // EMBEDDED COLLECTIONVIEW
var events = [Events]()
override func awakeFromNib() {
super.awakeFromNib()
// EMBEDDED BUTTON
ViewAllButton.setIcon(prefixText: "View All ", prefixTextColor: .blue, icon: .typIcons(.chevronRight), iconColor: .blue, postfixText: " ", forState: .normal, iconSize: 24) // EMBEDDED BUTTON
EventCollection.delegate = self
EventCollection.dataSource = self
}
extension PopularCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return events.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = EventCollection.dequeueReusableCell(withReuseIdentifier: "EventCell", for: indexPath) as! EventCell
let event = events[indexPath.row]
print("Event Name:\(event.event_name)")
cell.event = event
return cell
}
}
Here is the ViewController(TableView Delegate & DataSource) class :
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return groupedEventArray.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 245
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PopularCell", for: indexPath) as! PopularCell
let (category, events) = groupedEventArray[indexPath.row]
cell.ViewAllButton.addTarget(self, action: #selector(VenueViewController.ViewAll(_:)), for: .touchUpInside)
cell.PopularEvents.text = category
cell.events = events
return cell
}
#objc func ViewAll(_ sender:UIButton!) {
self.performSegue(withIdentifier: "ViewEvents", sender: sender)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "ViewEvents") {
let destination = segue.destination as? EventListController
destination?.navigationItem.title = "Event Category"
}
else if(segue.identifier == "ViewEventDetails") {
if let collectionCell: EventCell = sender as? EventCell {
if let _: UICollectionView = collectionCell.superview as? UICollectionView {
let destination = segue.destination as? EventDetailViewController
destination?.navigationItem.title = "Event Details"
}
}
}
}
How do I configure the func prepare so as to pass data through the "ViewEvents" & "ViewEventDetails" segue identifiers accordingly
How can I pass an image array from the first controller (collectionCell) to the second controller (collectionCell) using sg_setImage and class (swift file) using segue?
To load an array of images onto the first controller, I use collectionCell inside tableCell.
ViewController (firstVC):
class ViewController1: UIViewController {
#IBOutlet weak var tableView: UITableView!
var image: [Model] = []
var ref: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference(withPath: "Студии2")
ref.observe(.value, with: { (snapshot) in
var newImages: [Model] = []
for item in snapshot.children {
let object = Model(snapshot: item as! FIRDataSnapshot)
newImages.append(object)
}
self.image = newImages
self.tableView.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue1" {
if let indexPaths = self.tableView.indexPathForSelectedRow {
let indexPath = indexPaths as NSIndexPath
let dvc = segue.destination as! ViewController2
dvc.photo = [image[indexPath.row]]
}
}
}
}
extension ViewController1: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return image.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell1
cell.images = [image[indexPath.row].image,
image[indexPath.row].image2]
return cell
}
}
tableViewCell (firstVC):
class TableViewCell1: UITableViewCell {
var images = [String]()
#IBOutlet weak var collectionView: UICollectionView!
}
extension TableViewCell1: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell1
cell.imagesView.sd_setImage(with: URL(string: images[indexPath.item]))
return cell
}
}
collectionViewCell (secondVC):
class ViewController2: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var photo: [Model] = []
var ref: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController2: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photo.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell2
let array = [photo[indexPath.item].image,
photo[indexPath.item].image2]
cell.imagesView.sd_setImage(with: URL(string: array[indexPath.item]!))
// cell.imagesView.sd_setImage(with: URL(string: photo[indexPath.item].image2))
return cell
}
}
class Model (swift file):
class Model {
var image: String!
var image2: String!
var images: [String] = []
var images2: [String] = []
var ref: FIRDatabaseReference!
init(snapshot: FIRDataSnapshot) {
ref = snapshot.ref
let value = snapshot.value as! NSDictionary
let snap1 = value["hall1"] as? NSDictionary
let snap2 = value["hall2"] as? NSDictionary
image = snap1?["qwerty"] as? String ?? ""
image2 = snap2?["qwerty"] as? String ?? ""
if let post1 = snap1 as? [String: AnyObject] {
for (_, value) in post1["images"] as! [String: AnyObject] {
self.images.append(value as! String)
}
}
if let post1 = snap1 as? [String: AnyObject] {
for (_, value) in post1["images"] as! [String: AnyObject] {
self.images2.append(value as! String)
}
}
}
}
I actually have a view controller in which i am showing product attributes in a table view in the lower half of the View Controller and was showing product thumbnail in table view header. But then i realised that a product can have multiple thumbnails so i should add a collection view in upper half of view controller to show all those thumbnail (scrollable horizontally). I added both datasource and delegate for Collection view (UICollectionViewDataSource, UICollectionViewDelegate) and wrote functions to return number of section, number of rows and cellAtIndex but these functions are not called.
So my query is can i have both collection view and table view in same view controller? If yes, then how?
I am using iOS 8 SDK with swift
class ViewController: UIViewController {
#IBOutlet weak var collView: UICollectionView!
#IBOutlet weak var tblView: UITableView!
var arrMain = NSMutableArray()
var arrDays = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
var arrSunday = ["No Data Available"]
var arrMonday = ["1","2","3"]
var arrTuesday = ["A","B","C"]
var arrWednesday = ["a","b"]
var arrThursday = ["d","e","f"]
var arrFriday = ["5","6","7"]
var arrSaturdsay = ["X","y","z"]
override func viewDidLoad() {
super.viewDidLoad()
arrMain = (arrSunday as NSArray).mutableCopy() as! NSMutableArray
}
}
extension ViewController: UICollectionViewDelegate,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrDays.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collView.dequeueReusableCell(withReuseIdentifier: "collCell", for: indexPath) as! collCell
cell.lblDays.text = arrDays[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
arrMain = (arrSunday as NSArray).mutableCopy() as! NSMutableArray
}
else if indexPath.item == 1 {
arrMain = (arrMonday as NSArray).mutableCopy() as! NSMutableArray
}
else if indexPath.item == 2 {
arrMain = (arrTuesday as NSArray).mutableCopy() as! NSMutableArray
}
else if indexPath.item == 3 {
arrMain = (arrWednesday as NSArray).mutableCopy() as! NSMutableArray
}
else if indexPath.item == 4 {
arrMain = (arrThursday as NSArray).mutableCopy() as! NSMutableArray
}
else if indexPath.item == 5 {
arrMain = (arrFriday as NSArray).mutableCopy() as! NSMutableArray
}
else if indexPath.item == 6 {
arrMain = (arrSaturdsay as NSArray).mutableCopy() as! NSMutableArray
}
tblView.reloadData()
}
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrMain.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tblView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! tblCell
cell.lblName.text = arrMain[indexPath.row] as? String
return cell
}
}
class tblCell: UITableViewCell{
#IBOutlet weak var lblName: UILabel!
}
class collCell : UICollectionViewCell{
#IBOutlet weak var lblDays: UILabel!
}