PerformSegue from UICollectionReusableView(Header) custom button Swift - ios

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
}

Related

Error passing data to tableViewController

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]
}
}

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

Passing data with segue in the tableViewCell

I want to passing data with segue in the tableViewCell,from BulletinBoadrViewController to BbDetailViewController
class BulletinBoadrViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var bulletinBoards = [BulletinBoard]()
override func viewDidLoad() {
super.viewDidLoad()
bulletinBoards = BulletinBoard.downloadAllBulletinBoard()
self.tableView.reloadData()
tableView.estimatedRowHeight = tableView.rowHeight
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorStyle = .none
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bulletinBoards.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BulletinBoardTableViewCell
let bulletinBoard = bulletinBoards[indexPath.row]
cell.bulletinBoard = bulletinBoard
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "gotodetail", sender: indexPath)
print("Row \(indexPath.row)selected")
}
func prepareForSegue(segue: UIStoryboardSegue, sender: Any!) {
if segue.identifier == "gotodetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destVC = segue.destination as! BdDeatilViewController
let new = bulletinBoards[indexPath.row]
destVC.bulletinBoard = new
}
}
}
and it's BdDeailViewController
class BdDeatilViewController:UIViewController {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var contentLabel: UITextView!
#IBAction func backtobb(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
var x = [BulletinBoard]()
var bulletinBoard : BulletinBoard!{
didSet{
self.updateUI()
}
}
func updateUI() {
timeLabel.text = bulletinBoard.time
titleLabel.text = bulletinBoard.title
contentLabel.text = bulletinBoard.content
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
and tableViewCell's data is taking from local json file, it's BulletinBoard code
class BulletinBoard {
var title:String?
var time:String?
var content:String?
init(title:String,time:String,content:String) {
self.title = title
self.time = time
self.content = content
}
init(bulletinBoardDictionary:[String:Any]) {
self.title = bulletinBoardDictionary["title"] as? String
self.time = bulletinBoardDictionary["time"] as? String
self.content = bulletinBoardDictionary["content"] as? String
}
static func downloadAllBulletinBoard() -> [BulletinBoard] {
var bulletinBoards = [BulletinBoard]()
//get the json data from the file
let jsonFile = Bundle.main.path(forResource: "BulletinBoardData", ofType: "json")
let jsonFileURL = URL(fileURLWithPath: jsonFile!)
let jsonData = try? Data(contentsOf: jsonFileURL)
//turn the json data into foundation objects (bulletinBoards)
if let jsonDictionary = NetworkService.parseJSONFromData(jsonData) {
let bulletinBoardDictionaries = jsonDictionary["BulletinBoard"] as! [[String:Any]]
for bulletinBoardDictionary in bulletinBoardDictionaries {
let newBulletinBoard = BulletinBoard(bulletinBoardDictionary: bulletinBoardDictionary)
bulletinBoards.append(newBulletinBoard)
}
}
return bulletinBoards
}
}
Finally,it's my StoryBoard
https://i.stack.imgur.com/JcgdH.png1
Can anyone solve my problem?Thanks!
I think you should retrieve indexPath from sender in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: Any!) {
if segue.identifier == "gotodetail" {
if let indexPath = sender as? IndexPath {
let destVC = segue.destination as! BdDeatilViewController
let new = bulletinBoards[indexPath.row]
destVC.bulletinBoard = new
}
}
}
and update the UI in the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.updateUI()
}

Showing optional value as 'nil' while changing the viewController from UITableView

I'm trying to display details from a table row onto another viewController, there are three entities which I want to display on the second VC. Two UILabel and one UIImageView. While in the first VC I'm able to view, when in the second VC, it says 'Optional("")', And don't know how to unwrap it.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate,UITableViewDataSource, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var nameForUser: UITextField!
#IBOutlet var loginButton: UIButton!
#IBOutlet var tableView: UITableView!
let allEvents = Events.allEvents
var nextScreenRow: Events!
override func viewDidLoad() {
super.viewDidLoad()
//nameForUser.text! = "Please Enter Name Here"
// Do any additional setup after loading the view, typically from a nib.
//let userName = nameForUser.text
}
func textFieldDidBeginEditing(textField: UITextField) {
textField.text = ""
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.allEvents.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("eventsCell")!
let event = self.allEvents[indexPath.row]
//print(" row \(indexPath.row)")
cell.textLabel?.text = event.eventName
cell.imageView?.image = UIImage(named: event.imageName)
cell.detailTextLabel?.text = event.entryType
//cell.textLabel?.text = allEvents[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
nextScreenRow = allEvents[indexPath.row]
let view : DetailedEventViewController = self.storyboard?.instantiateViewControllerWithIdentifier("trytry") as! DetailedEventViewController
self.navigationController?.pushViewController(view, animated: true)
print("Selected section \(indexPath.section), row \(indexPath.row)")
print(nextScreenRow.eventName)
view.eventDetails = nextScreenRow.eventName
print(view.eventDetails)
view.typeOfEvent = nextScreenRow.entryType
view.myImage = UIImage(named: nextScreenRow.imageName)
//even the 'print' is used, it is displaying here, but not in the next VC
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.allEvents.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let sec = collectionView.dequeueReusableCellWithReuseIdentifier("eventsSec", forIndexPath: indexPath) as! GridCollectionViewCell
let event = self.allEvents[indexPath.row]
sec.imageView.image = UIImage(named: event.imageName)
sec.caption.text = event.entryType
return sec
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("tryToConnect2", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "successfulLogin"){
segue.destinationViewController as! TabController
//let userName = nameForUser.text
//controller.userName = userName
}
}
#IBAction func loginButtonWhenPressed(sender: UIButton) {
let userName = nameForUser.text
if userName == "" {
let nextController = UIAlertController()
nextController.title = "Error!"
nextController.message = "Please enter a name"
let okAction = UIAlertAction(title: "okay", style: UIAlertActionStyle.Default) {
action in self.dismissViewControllerAnimated(true, completion: nil)
}
nextController.addAction(okAction)
self.presentViewController(nextController, animated: true, completion: nil)
}
}
}
And here is my second VC:
import UIKit
class DetailedEventViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var deatiledEvent: UILabel!
#IBOutlet weak var eventType: UILabel!
var eventDetails = ""
var typeOfEvent = ""
var myImage: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func viewTheElemnts(sender: AnyObject) {
deatiledEvent.text = eventDetails
print(deatiledEvent.text)
eventType.text = typeOfEvent
imageView.image = myImage
}
}
Help would be appreciated greatly. Thank you.
You are creating an instance of DetailedEventViewController in the didSelectRowAtIndexPath and setting the value there. But actually you are moving to DetailedEventViewController using segue. So you should add those values in the prepareForSegue: method.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "trytry")
{
let selectedIndex = self.tableView.indexPathForCell(sender as! UITableViewCell)
nextScreenRow = allEvents[selectedIndex.row]
let view = segue.destinationViewController as! DetailedEventViewController
view.eventDetails = nextScreenRow.eventName
view.typeOfEvent = nextScreenRow.entryType
view.myImage = UIImage(named: nextScreenRow.imageName)
}
}
Unwrap your eventName like this :
view.eventDetails = nextScreenRow.eventName! as String
print(view.eventDetails) view.typeOfEvent = nextScreenRow.entryType
view.myImage = UIImage(named: nextScreenRow.imageName)
self.navigationController?.pushViewController(view, animated: true

Resources