Error passing data to tableViewController - ios

I have a problem. I'm making an app that consumes an API from themoviedb and saves the movies in the Realm. On the main screen I am using the CollectionViewController, and when I touch the movie it will go to details. However when it goes to details nothing appears and when I select another film it brings the previous film.
Home:
import UIKit
import RealmSwift
class HomeViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UISearchBarDelegate {
#IBOutlet weak var searchbar: UISearchBar!
#IBOutlet weak var ListOfMovies: UICollectionView!
var movies : Results<Moviess>!
let realm = try! Realm()
var detailsMovies = Moviess()
override func viewDidLoad() {
super.viewDidLoad()
print(Realm.Configuration.defaultConfiguration.fileURL!)
ListOfMovies.delegate = self
ListOfMovies.dataSource = self
searchbar.delegate = self
movies = realm.objects(Moviess.self)
getRequest()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func getObjects(filter: String) -> Results<Moviess>{
movies = realm.objects(Moviess.self).filter("title CONTAINS[cd] %#",filter)
return movies
}
func getRequest(){
print(movies.count)
if movies.count < 1 {
RequestData.requisicao { (result) in
switch(result){
case .success(let detalhe):
self.ListOfMovies.reloadData()
print(detalhe)
case .failure(let error):
print(error)
}
}
}}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return movies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listOfMovies", for: indexPath) as! HomeCollectionViewCell
let infos = movies[indexPath.item]
cell.configurationMovie(movie: infos)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
detailsMovies = movies[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return UIDevice.current.userInterfaceIdiom == .phone ? CGSize(width: collectionView.bounds.width/2-20, height: 200) : CGSize(width: collectionView.bounds.width/3-20, height: 250)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
if !searchText.isEmpty{
let filtro = getObjects(filter: searchText)
print(filtro)
ListOfMovies.reloadData()
}else{
movies = realm.objects(Moviess.self)
ListOfMovies.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "details")
{
let viewController = segue.destination as! DetailViewController
print(detailsMovies)
viewController.conta = detailsMovies
}
}
}
Details:
import UIKit
import RealmSwift
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var conta: Moviess!
let realm = try! Realm()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return [conta].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "details", for: indexPath) as! DetailsTableViewCell
let infos = [conta][indexPath.item]
cell.prepare(movie: infos!)
return cell
}
}
Moviess
import RealmSwift
import UIKit
class Moviess: Object{
#objc dynamic var id = 0
#objc dynamic var title = ""
#objc dynamic var overview = ""
#objc dynamic var poster = ""
#objc dynamic var isFavorites = false
override class func primaryKey() -> String? {
return "id"
}
convenience init (id: Int){
self.init()
self.id = id
}
override class func indexedProperties() -> [String] {
return ["isFavorites"]
}
func insertMovieData(list: Moviess){
do {
let realm = try! Realm()
try! realm.write({ () -> Void in
realm.add(list)
})
} catch let error as NSError{
print("insert error : \(error)")
}
}
func togleFavorite(){
try? realm?.write{
isFavorites = !isFavorites
}
}
}
I have no idea where I might be giving this "bug". If someone can help and explain what is going on it will be very useful.

Your problem is that prepare(for:sender:) is called before collectionView(_:, didSelectItemAt:). prepare(for:sender:) should not rely on collectionView(_:, didSelectItemAt:).
Your prepare(for:sender:) method should look like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UICollectionViewCell,
let indexPath = ListOfMovies.indexPath(for: cell) else { return }
if (segue.identifier == "details") {
let viewController = segue.destination as! DetailViewController
viewController.conta = movies[indexPath.item]
}
}

Related

How do I set auto height on my CollectionViewCell

I've been searching for days these answers and some how I found it but I don't know how to configure properly to my project.
I followed this awesome video iOS Dev 11: CollectionViews | Swift 5, XCode 11
This video works perfectly to me, but when we reach sizeForItemAt method he declares a static value. For my project, width could be static and I was able to fix according the result I want, but I am still facing difficult to adapt the height. I want it to be equal the text size of my label text, I want I result like this: final result
So, I know that I need to to this on sizeForItemAt method but I don't know how, here is how my code looks like:
FactsCollectionViewCell:
class FactsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var lbFactsText: UILabel!
#IBOutlet weak var lbCategories: UILabel!
func setup(with facts: FactsData?, index: Int) {
let result = facts!.result[index]
lbFactsText.text = result.value
print(result.value)
if let category = result.categories.first {
lbCategories.text = category!.uppercased()
lbCategories.sizeToFit()
} else {
lbCategories.text = "UNCATEGORIZED"
lbCategories.sizeToFit()
}
}
#IBAction func btShare(_ sender: UIButton) {
// still working on this button
}
}
And this is my MainViewController:
class MainViewController: UIViewController {
var facts: FactsData?
var factsManager = FactsManager()
var numberOfCV = 0
var resultValue: String = ""
#IBOutlet weak var cvFacts: UICollectionView!
#IBOutlet weak var sbSearchFacts: UISearchBar!
#IBOutlet weak var nbMainScreen: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
cvFacts.dataSource = self
cvFacts.delegate = self
cvFacts.collectionViewLayout = UICollectionViewFlowLayout()
sbSearchFacts.isHidden = true
}
#IBAction func search(_ sender: UIBarButtonItem) {
sbSearchFacts.resignFirstResponder()
sbSearchFacts.isHidden = !sbSearchFacts.isHidden
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueMainToFact" {
let vc = segue.destination as! FactViewController
vc.receivedValue = resultValue
}
}
}
extension MainViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numberOfCV
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = cvFacts.dequeueReusableCell(withReuseIdentifier: "FactsCollectionViewCell", for: indexPath) as! FactsCollectionViewCell
cell.setup(with: facts, index: indexPath.row)
return cell
}
}
// SearchBar delegate extension for when tap to search button
extension MainViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
sbSearchFacts.isHidden = true
sbSearchFacts.resignFirstResponder()
if let realFact = sbSearchFacts.text {
factsManager.factsRequest(realFact) { (facts) in
self.facts = facts
self.numberOfCV = self.facts!.total
DispatchQueue.main.async {
self.cvFacts.reloadData()
}
} onError: { (error) in
print(error)
}
}
}
}
// Extension to configure cell sizes
extension MainViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.size.width
return CGSize(width: width - 40, height: //HERE I NEED TO CALCULATE THE HEIGHT BUT I DONT KNOW HOW)
}
}
// Extension for when you tap the cell
extension MainViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let index = indexPath.row
let result = facts?.result[index]
resultValue = result!.value
}
}
Does anyone has a tip to fix it?

How to pass data with delegate from footer cell to view controller?

Ive been stuck trying to pass data from the FoodEatenController(FEC) Footer to the TotalCaloriesController. The code that I have now it shows NOTHING in the calorieLbl of the TotalCalorieController(TCC).
The delegate that ive been using to pass the data from the FEC to the TCC does not pass the text/string data that is in the FoodFooter calorieTotallbl to the TEC calorieLbl
the data that populates the cells of the FEC is retrieved from Cloud Firestore and passed in from anotherView Controller (FoodPickerController)
import UIKit
class FoodEatenController: UIViewController{
var selectedFood: FoodList! // allows data to be passed into the VC
// allows data to be sepearted into sections
var foodItems: [FoodItem] = []
var groupedFoodItems: [String: [FoodItem]] = [:]
var dateSectionTitle: [String] = []
#IBOutlet weak var tableView: UITableView!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? TotalCalorieController {
}
}
}
extension FoodEatenController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return dateSectionTitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let date = dateSectionTitle[section]
return groupedFoodItems[date]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let foodCell = tableView.dequeueReusableCell(withIdentifier: "FoodCell") as! FoodCell
let date = dateSectionTitle[indexPath.section]
let foodItemsToDisplay = groupedFoodItems[date]![indexPath.row]
foodCell.configure(withCartItems: fooditemsToDisplay.foodList)
return foodCell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let foodHeader = tableView.dequeueReusableCell(withIdentifier: "FoodHeader") as! FoodHeader
let headerTitle = dateSectionTitle[section]
foodHeader.dateLbl.text = "Date: \(headerTitle)"
return foodHeader
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let foodFooter = tableView.dequeueReusableCell(withIdentifier: "FoodFooter") as! FoodFooter
let date = dateSectionTitle[section]
let arrAllItems = groupedFoodItems[date]!
var total: Float = 0
for item in arrAllItems {
let eaten = item.productList
let selectedMeal = item.foodList.selectedOption
if selectedMeal == 1 {
total = total + (Float(eaten!.calorie))
}
}
foodFooter.calorieTotal.text = String(subtotal!)
foodFooter.delegate = self
return foodFooter
}
}
extension FoodEatenController: EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
let popUp = self.storyboard?.instantiateViewController(withIdentifier: "AdditionalCostsVC") as! TotalCalorieController
popUp.calorieLbl.text = info
}
}
import UIKit
protocol EatenFoodDelegate: class {
func onTouchCaloireInfo(info: String)
}
class FoodFooter: UITableViewCell {
weak var delegate: EatenFoodDelegate? = nil
#IBOutlet weak var calorieTotal: UILabel!
#IBOutlet weak var totalInfoBtn: UIButton!
#IBAction func totalOnClicked(_ sender: AnyObject) {
self.delegate?. onTouchCaloireInfo(info: calorieTotal.text!)
}
}
class TotalCalorieController: UIViewController, EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
calorieLbl.text = info
}
#IBOutlet weak var calorieLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func returnButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
print("Close Taxes and Fees")
}
}
Add the following line at the end of the func onTouchCaloireInfo(info:)
self.present(popUp, animated: true, completion: nil)
If you would like to be sure that the function onTouchCaloireInfo(info:) gets called, just add the following line:
debugPrint("onTouchCaloireInfo")
And check, if it prints the given string in the console of the Xcode
extension FoodEatenController: EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
debugPrint("onTouchCaloireInfo")
let popUp = self.storyboard?.instantiateViewController(withIdentifier: "AdditionalCostsVC") as! TotalCalorieController
self.present(popUp, animated: true) {
popUp.calorieLbl.text = info
}
}
}

How to pass data between custom table view cells?

I have two custom table views (not table view controller) with custom
cells. I want to select a second cell from MainViewcontroller
and selected
title and price of cell info pass to the second view as
DetailViewController's first and second index labels.
This is my MainViewController.swift
Edit: Here is the answer
import UIKit
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate,
UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var mainTableView: UITableView!
var imageNames = [ImageNames]()
var searchFoods: [String]!
var priceFood: [Double]!
var searching = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = false
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isHidden = true
let foodCell = Food(name: ["Hamburger big mac",
"Patates",
"Whopper",
"Steakhouse"], price: [15.0, 20.0, 25.0, 30.0])
searchBar.delegate = self
searchFoods = foodCell.name
priceFood = foodCell.price
imageNames = [
ImageNames(name: "images"),
ImageNames(name: "unnamed"),
ImageNames(name: "unnamed")
]
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? 1 : searchFoods.count
// return section == 0 ? 1 : foodNames.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return indexPath.section == 0 ? 130 : 65
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return indexPath.section == 0 ? 100 : 65
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainFoodTableViewCell", for: indexPath) as! MainFoodTableViewCell
cell.mainFoodCollectionView.delegate = self
cell.mainFoodCollectionView.dataSource = self
cell.mainFoodCollectionView.reloadData()
cell.mainFoodCollectionView.tag = indexPath.row
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellForFood", for: indexPath) as! MainFoodTitleTableViewCell
cell.titleLabel?.text = searchFoods[indexPath.row]
cell.priceLabel?.text = priceFood[indexPath.row].description
return cell
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cellForFoodSegue" {
if let destinationViewController = segue.destination as? DetailViewController
{
let indexPath = self.mainTableView.indexPathForSelectedRow!
var foodNameArray: [String]
var foodPriceArray: [Double]
foodNameArray = [searchFoods[indexPath.row]]
foodPriceArray = [priceFood[indexPath.row]]
destinationViewController.detailFoodName = foodNameArray
destinationViewController.detailFoodPrice = foodPriceArray
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageNames.count
}
//MARK:- collection view cell size
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = UIScreen.main.bounds.width
return CGSize(width: width, height: 130)
}
//MARK:- //collection view cell data
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainFoodCollectionViewCell", for: indexPath) as! MainFoodCollectionViewCell
let img = imageNames[indexPath.row]
cell.mainFoodImage.image = UIImage(named: img.name)
return cell
}
}
//MARK:- SearchBar data
extension MainViewController : UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.searchBar.showsCancelButton = true
mainTableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
mainTableView.reloadData()
searchBar.showsCancelButton = false
searchBar.text = ""
searchBar.resignFirstResponder()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchFoods = searchText.isEmpty ? searchFoods : searchFoods.filter { (item: String) -> Bool in
return item.range(of: searchText, options: .caseInsensitive, range: nil, locale: nil) != nil
}
mainTableView.reloadData()
}
}
Edit: Here is my DetailViewController
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var foodTitle: UILabel!
#IBOutlet weak var foodSubTitle: UILabel!
#IBOutlet weak var foodPiece: UILabel!
#IBOutlet weak var foodPrice: UILabel!
#IBOutlet weak var drinkPicker: UITextField!
#IBOutlet weak var menuPieceStepper: UIStepper!
var drinkPickerView = UIPickerView()
var selectDrinkType: [String] = []
var detailFoodName : [String] = []
var detailFoodPrice : [Double] = [0.0]
let foods = Food(name: ["Hamburger big mac",
"Patates",
"Whopper",
"Steakhouse"], price: [15.0, 20.0, 25.0, 30.0])
#IBAction func foodPieceStepper(_ sender: Any) {
}
#objc func foodPieceChangeStepper() {
let res = menuPieceStepper.value + foods.price.first!
foodPrice.text = "\(res)"
}
#IBAction func addBasket(_ sender: Any) {
let destinationVC = MyCartViewController()
destinationVC.fromDetailFoodNames = foods.name
destinationVC.fromDetailFoodPrices = foods.price
dismiss(animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "addToCartSegue") {
if let addToCartVC = segue.destination as? MyCartViewController {
addToCartVC.fromDetailFoodNames = [foodTitle.text]
addToCartVC.fromDetailFoodPrices = foods.price
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
menuPieceStepper.value = 0.0
menuPieceStepper.minimumValue = 0.0
menuPieceStepper.maximumValue = 30.0
menuPieceStepper.stepValue = foods.price.first!
menuPieceStepper.addTarget(self, action: #selector(foodPieceChangeStepper), for: .valueChanged)
drinkPickerView.delegate = self
drinkPicker.inputView = drinkPickerView
selectDrinkType = ["Ayran", "Kola", "Su", "Fanta", "Şalgam", "Sprite"]
foodTitle.text = detailFoodName.description
foodPrice.text = detailFoodPrice.description
self.navigationController?.navigationItem.title = "Sipariş Detayı"
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard (_:)))
self.view.addGestureRecognizer(tapGesture)
}
#objc func dismissKeyboard (_ sender: UITapGestureRecognizer) {
drinkPicker.resignFirstResponder()
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = false
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = true
}
}
extension DetailViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return selectDrinkType.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return selectDrinkType[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let selectedDrink = selectDrinkType[row]
drinkPicker.text = selectedDrink
}
}
Aren't you looking for prepare(for segue: sender:) method? Usually you need to bind a cell prototype to a detail view with a segue, and when the cell is selected the segue is followed just after that method is called to prepare data injection to the detail view.

Perform Segue from UICollectionViewCell

So I'm creating a blog app, and on the home news feed collection view (imageCollection, loaded from firebase database) I have a button. This button title depends on the Category of the image. What i'm having an issue with is performing the segue in the UICollectionViewCell class. I ran the button action with the print statement, and it worked. But when i try to add performSegue, well it doesn't let me. (Use of unresolved identifier 'performSegue')
Any tips? thank you!
P.S. i'm still fairly new to swift, so if i come off a little ignorant, i apologize
My ViewController
import UIKit
import Firebase
import FirebaseDatabase
import SDWebImage
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var popImageCollection: UICollectionView!
#IBOutlet weak var imageCollection: UICollectionView!
var customImageFlowLayout = CustomImageFlowLayout()
var popImageFlowLayout = PopImagesFlowLayout()
var images = [BlogInsta]()
var popImageArray = [UIImage]()
var homePageTextArray = [NewsTextModel]()
var dbRef: DatabaseReference!
var dbPopularRef: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
dbRef = Database.database().reference().child("images")
dbPopularRef = Database.database().reference().child("popular")
loadDB()
loadImages()
loadText()
customImageFlowLayout = CustomImageFlowLayout()
popImageFlowLayout = PopImagesFlowLayout()
imageCollection.backgroundColor = .white
popImageCollection.backgroundColor = .white
// Do any additional setup after loading the view, typically from a nib.
}
func loadText() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.homePageTextArray.removeAll()
for homeText in snapshot.children.allObjects as! [DataSnapshot] {
let homeTextObject = homeText.value as? [String: AnyObject]
let titleHome = homeTextObject?["title"]
let categoryButtonText = homeTextObject?["category"]
self.imageCollection.reloadData()
let homeLabels = NewsTextModel(title: titleHome as! String?, buttonText: categoryButtonText as! String?)
self.homePageTextArray.append(homeLabels)
}
}
})
}
func loadImages() {
popImageArray.append(UIImage(named: "2")!)
popImageArray.append(UIImage(named: "3")!)
popImageArray.append(UIImage(named: "4")!)
self.popImageCollection.reloadData()
}
func loadDB() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
var newImages = [BlogInsta]()
for BlogInstaSnapshot in snapshot.children {
let blogInstaObject = BlogInsta(snapshot: BlogInstaSnapshot as! DataSnapshot)
newImages.append(blogInstaObject)
}
self.images = newImages
self.imageCollection.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.imageCollection {
return images.count
} else {
return popImageArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollection {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
let image = images[indexPath.row]
let text: NewsTextModel
text = homePageTextArray[indexPath.row]
cell.categoryButton.setTitle(text.buttonText, for: .normal)
cell.newTitleLabel.text = text.title
cell.imageView.sd_setImage(with: URL(string: image.url), placeholderImage: UIImage(named: "1"))
return cell
} else {
let cellB = popImageCollection.dequeueReusableCell(withReuseIdentifier: "popCell", for: indexPath) as! PopularCollectionViewCell
let popPhotos = popImageArray[indexPath.row]
cellB.popularImageView.image = popPhotos
cellB.popularImageView.frame.size.width = view.frame.size.width
return cellB
}
}
}
My ImageCollectionViewCell.swift
import UIKit
import Foundation
class ImageCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var categoryButton: UIButton!
#IBOutlet weak var newTitleLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBAction func categoryButtonAction(_ sender: Any) {
if categoryButton.currentTitle == "Fashion" {
print("Fashion Button Clicked")
performSegue(withIdentifier: "homeToFashion", sender: self)
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.imageView.image = nil
}
}
You need a custom delegate. Do this:
protocol MyCellDelegate {
func cellWasPressed()
}
// Your cell
class ImageCollectionViewCell: UICollectionViewCell {
var delegate: MyCellDelegate?
#IBAction func categoryButtonAction(_ sender: Any) {
if categoryButton.currentTitle == "Fashion" {
print("Fashion Button Clicked")
self.delegate?.cellWasPressed()
}
}
}
// Your viewcontroller must conform to the delegate
class ViewController: MyCellDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollection {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
// set the delegate
cell.delegate = self
// ...... rest of your cellForRowAtIndexPath
}
// Still in your VC, implement the delegate method
func cellWasPressed() {
performSegue(withIdentifier: "homeToFashion", sender: self)
}
}
You should use your own delegate. It is already described here
performSegue(withIdentifier:sender:) won't work from cell because it is UIViewController metod.
also you can make use of closure

PerformSegue from UICollectionReusableView(Header) custom button Swift

I have a headerView in which I am having a float button. Since all the subitems in the float button are programmatically generated and it is a subview of headerView I am not able to call a performSegue. I tried creating a delegate method, But I totally messed it up. I know that protocols might be the fix, But i am quite unsure about how to exactly work it out.
I am attaching the image of the headerView below
headerView code :
class ProfileHeader: UICollectionReusableView, FloatyDelegate {
#IBOutlet weak var pname: UILabel!
#IBOutlet weak var pusername: UILabel!
#IBOutlet weak var profilePic: UIImageView!
#IBOutlet weak var userDesc: UILabel!
#IBOutlet weak var allviews: UILabel!
#IBOutlet weak var allphotos: UILabel!
var user : User!
var userposts = [String]()
var currentViewUser: String!
var floaty = Floaty()
let uid = KeychainWrapper.standard.string(forKey: KEY_UID)
override func awakeFromNib() {
super.awakeFromNib()
user = User()
fetchCurrentUser()
profilePic.addBlackGradientLayer(frame: profilePic.bounds)
layoutFAB()
}
func layoutFAB() {
floaty.openAnimationType = .slideDown
floaty.hasShadow = false
floaty.addItem("Edit Profile", icon: UIImage(named: "")) { item in
// here is where the segue is to be performed.
}
floaty.paddingY = (frame.height - 50) - floaty.frame.height/2
floaty.fabDelegate = self
floaty.buttonColor = UIColor.white
floaty.hasShadow = true
floaty.size = 45
addSubview(floaty)
}
func fetchCurrentUser(){
if uid != nil {
FBDataservice.ds.REF_CURR_USER.observeSingleEvent(of: .value, with: { (snapshot) in
if let allData = snapshot.value as? Dictionary<String, Any> {
if let cred = allData["credentials"] as? Dictionary<String, Any> {
let user = User(userid: snapshot.key, userData: cred)
self.pusername.text = user.username
self.pname.text = user.name
self.userDesc.text = user.aboutme
self.allphotos.text = String(user.photos)
self.allviews.text = String(user.views)
if user.userPic != nil {
let request = URL(string: user.userPic)
Nuke.loadImage(with: request!, into: self.profilePic)
} else {
return
}
}
}
})
}
}
}
CollectionViewController code:
class ProfileVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
var user : User!
var userposts = [String]()
var post = [Posts]()
var currentViewUser: String!
var imagePicker: UIImagePickerController!
var fetcher: Fetcher!
var imageFromImagePicker = UIImage()
let uid = KeychainWrapper.standard.string(forKey: KEY_UID)
override func viewDidLoad() {
super.viewDidLoad()
user = User()
fetcher = Fetcher()
imagePicker = UIImagePickerController()
imagePicker.delegate = self
collectionView.delegate = self
collectionView.dataSource = self
initializeUserPost()
let nib = UINib(nibName: "ProfileCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: "ProfileCell")
}
func editProfileTapped() {
performSegue(withIdentifier: "manageconnections", sender: nil)
}
#IBAction func manageConnections(_ sender: Any) {
performSegue(withIdentifier: "manageconnections", sender: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
collectionView.reloadData()
}
#IBAction func gotoSettings(_ sender: Any) {
performSegue(withIdentifier: "openSettings", sender: nil)
}
#IBAction func newPost(_ sender: Any) {
uploadNewPost()
}
#IBAction func backToFeed(_ sender: Any) {
performSegue(withIdentifier: "feedroute", sender: nil)
}
#IBAction func searchUsers(_ sender: Any) {
performSegue(withIdentifier: "searchNow", sender: nil)
}
func initializeUserPost() {
FBDataservice.ds.REF_POSTS.observe(.value, with: { (snapshot) in
if let snap = snapshot.value as? Dictionary<String, Any> {
for snapy in snap {
if let userimages = snapy.value as? Dictionary<String, Any> {
let author = userimages["author"] as? String
if author! == self.uid! {
let images = userimages["imageurl"] as? String
self.userposts.append(images!)
}
}
}
}
self.collectionView.reloadData()
})
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let userImages = userposts[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProfileCell", for: indexPath) as! ProfileCell
cell.fillCells(uid: uid!, userPost: userImages)
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userposts.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selecteditem : String!
selecteditem = userposts[indexPath.row]
performSegue(withIdentifier: "lol", sender: selecteditem)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "lol" {
if let detailvc = segue.destination as? PhotoDetailVC {
if let bro = sender as? String {
detailvc.image = bro
}
}
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ProfileHeader", for: indexPath) as! ProfileHeader
return view
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (collectionView.bounds.size.width/3) - 1
print(width)
let size = CGSize(width: width, height: width)
return size
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
To solve your problem, you should use a pattern called delegate.
Delegate means that you need something to do the work of perform a segue for your HeaderView instead, something that can do it, in this case your ProfileVC.
1 - Create a protocol inside your HeaderView or another file, it's your call.
2 - Add a protocol variable type in your HeaderView as well and pass it from your viewController.
class ProfileHeader: UICollectionReusableView, FloatyDelegate {
#IBOutlet weak var pname: UILabel!
#IBOutlet weak var pusername: UILabel!
#IBOutlet weak var profilePic: UIImageView!
#IBOutlet weak var userDesc: UILabel!
#IBOutlet weak var allviews: UILabel!
#IBOutlet weak var allphotos: UILabel!
var user : User!
var userposts = [String]()
var currentViewUser: String!
var floaty = Floaty()
var delegate: HeaderViewDelegate!
let uid = KeychainWrapper.standard.string(forKey: KEY_UID)
override func awakeFromNib() {
super.awakeFromNib()
user = User()
fetchCurrentUser()
profilePic.addBlackGradientLayer(frame: profilePic.bounds)
layoutFAB()
}
func setDelegate(delegate: HeaderViewDelegate) {
self.delegate = delegate
}
func layoutFAB() {
floaty.openAnimationType = .slideDown
floaty.hasShadow = false
floaty.addItem("Edit Profile", icon: UIImage(named: "")) { item in
delegate.fabItemClicked()
}
floaty.paddingY = (frame.height - 50) - floaty.frame.height/2
floaty.fabDelegate = self
floaty.buttonColor = UIColor.white
floaty.hasShadow = true
floaty.size = 45
addSubview(floaty)
}
func fetchCurrentUser(){
if uid != nil {
FBDataservice.ds.REF_CURR_USER.observeSingleEvent(of: .value, with: { (snapshot) in
if let allData = snapshot.value as? Dictionary<String, Any> {
if let cred = allData["credentials"] as? Dictionary<String, Any> {
let user = User(userid: snapshot.key, userData: cred)
self.pusername.text = user.username
self.pname.text = user.name
self.userDesc.text = user.aboutme
self.allphotos.text = String(user.photos)
self.allviews.text = String(user.views)
if user.userPic != nil {
let request = URL(string: user.userPic)
Nuke.loadImage(with: request!, into: self.profilePic)
} else {
return
}
}
}
})
}
}
public protocol HeaderViewDelegate {
func fabItemClicked()
}
}
3 - Finally, edit your ProfileVC to implement the HeaderViewDelegate protocol and pass it to your HeaderView
class ProfileVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, HeaderViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var user : User!
var userposts = [String]()
var post = [Posts]()
var currentViewUser: String!
var imagePicker: UIImagePickerController!
var fetcher: Fetcher!
var imageFromImagePicker = UIImage()
let uid = KeychainWrapper.standard.string(forKey: KEY_UID)
override func viewDidLoad() {
super.viewDidLoad()
user = User()
fetcher = Fetcher()
imagePicker = UIImagePickerController()
imagePicker.delegate = self
collectionView.delegate = self
collectionView.dataSource = self
initializeUserPost()
let nib = UINib(nibName: "ProfileCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: "ProfileCell")
}
//Method from the new protocol
func fabItemClicked(){
//Perform your segue here.
}
func editProfileTapped() {
performSegue(withIdentifier: "manageconnections", sender: nil)
}
#IBAction func manageConnections(_ sender: Any) {
performSegue(withIdentifier: "manageconnections", sender: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
collectionView.reloadData()
}
#IBAction func gotoSettings(_ sender: Any) {
performSegue(withIdentifier: "openSettings", sender: nil)
}
#IBAction func newPost(_ sender: Any) {
uploadNewPost()
}
#IBAction func backToFeed(_ sender: Any) {
performSegue(withIdentifier: "feedroute", sender: nil)
}
#IBAction func searchUsers(_ sender: Any) {
performSegue(withIdentifier: "searchNow", sender: nil)
}
func initializeUserPost() {
FBDataservice.ds.REF_POSTS.observe(.value, with: { (snapshot) in
if let snap = snapshot.value as? Dictionary<String, Any> {
for snapy in snap {
if let userimages = snapy.value as? Dictionary<String, Any> {
let author = userimages["author"] as? String
if author! == self.uid! {
let images = userimages["imageurl"] as? String
self.userposts.append(images!)
}
}
}
}
self.collectionView.reloadData()
})
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let userImages = userposts[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProfileCell", for: indexPath) as! ProfileCell
cell.fillCells(uid: uid!, userPost: userImages)
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userposts.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selecteditem : String!
selecteditem = userposts[indexPath.row]
performSegue(withIdentifier: "lol", sender: selecteditem)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "lol" {
if let detailvc = segue.destination as? PhotoDetailVC {
if let bro = sender as? String {
detailvc.image = bro
}
}
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ProfileHeader", for: indexPath) as! ProfileHeader
view.delegate = self
return view
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (collectionView.bounds.size.width/3) - 1
print(width)
let size = CGSize(width: width, height: width)
return size
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
Don't forget to edit this method and pass self to your HeaderView:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ProfileHeader", for: indexPath) as! ProfileHeader
view.delegate = self
return view
}

Resources