How to pass data between custom table view cells? - ios

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.

Related

Stuck at reassigning TableView Cell property

I'm working on an App where you can track your reading progress for Books.
The HomeViewController contains a TableView that lists the books you have added. It has a progress Bar and shows what page you're on. The AddBookController is for adding Data about a book that gets delegated to the HomeViewController and is then listed as a TableView Row. The BookDetailController is shown when you select a Row and is for updating the Page you're on. I'll provide some screenshots.
I'm stuck at changing the "currentPage" property of the TableView Cell once you update it in the BookDetailViewController. I'm able to send the updatedPage to the HomeViewController (where the TableView is) but I don't know how to align the values of the Cells with the new value. My idea was to use an didSet-observer but I can't figure out how to set it up the right way.
Here is my code:
HomeViewController
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SendingBookDataProtocol {
var updatedPage = String() {
didSet { // probably at the wrong place but everything I've tried didn't work
print(updatedPage)
tableView?.reloadData()
}
}
var items = [BookItem]()
var item: BookItem?
override func viewDidLoad() {
super.viewDidLoad(){
tableView?.delegate = self
tableView?.dataSource = self
let nib = UINib(nibName: "BookCell", bundle: nil)
tableView?.register(nib, forCellReuseIdentifier: "BookCell")
}
func sendDataToHomeController(bookEntry item:BookItem) {
items.append(item)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let bookDetailVc = self.storyboard?.instantiateViewController(withIdentifier: "BookDetailView") as? BookDetailViewController
let item = items[indexPath.row]
let currentPageInt = Float(item.currentPage)!
let totalPagesInt = Float(item.totalPages)!
let result = Int(totalPagesInt - currentPageInt)
let percentageRead = Int((currentPageInt / totalPagesInt) * 100)
bookDetailVc?.lblName = item.title
bookDetailVc?.lblCurrentPage = Int(Float(item.currentPage)!)
// ...some more Code. Basically just sending the Data to BookDetailViewController
self.navigationController!.pushViewController(bookDetailVc!, animated: true)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell", for: indexPath) as! BookCell
item = items[indexPath.row]
cell.title.text = item?.title
//... and so on
cell.pageNumbers.text = "S. " + item!.currentPage + " / " + item!.totalPages //here currentPage needs to be updated
return cell
}
}
BookDetailViewController
class BookDetailViewController: HomeViewController, UIPickerViewDelegate, UIPickerViewDataSource{
#IBOutlet weak var bookTitle: UILabel!
//...
#IBOutlet weak var numberPicker: UIPickerView!
var lblName = String()
//...
var lblCurrentPage = Int()
override func viewDidLoad() {
super.viewDidLoad()
self.numberPicker.delegate = self
self.numberPicker.dataSource = self
//...
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let valueSelected = pickerData[row] as String
if let homeVc = storyboard?.instantiateViewController(withIdentifier: "getBookData") as? HomeViewController{
homeVc.updatedPage = valueSelected
homeVc.tableView?.reloadData()
}
}
}
AddBookController
protocol SendingBookDataProtocol {
func sendDataToHomeController(bookEntry: BookItem)
}
struct BookItem {
let title,author,currentPage,totalPages:String
let image: UIImage?
}
class AddBookController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var delegate: SendingBookDataProtocol? = nil
#IBAction func buttonSave(_ sender: Any) {
let bookEntry = BookItem(title: textfieldTitle.text!, author: textfieldAuthor.text!, currentPage: fieldCurrentPage.text!, totalPages: fieldTotalPages.text!, image: bookImage)
self.delegate?.sendDataToHomeController(bookEntry: bookEntry)
dismiss(animated: true, completion: nil)
}
}
// rest should be irrelevant
Here are the screenshots:
You have to create a delegate in BookDetailViewController, like this :
protocol BookDetailDelegate: AnyObject {
func updatePage(for bookItem : BookItem)
}
class BookDetailViewController: HomeViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var item: BookItem
var delegate: BookDetailDelegate?
//...
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let valueSelected = pickerData[row] as String
delegate?.updatePage(valueSelected)
}
}
And then in the HomeViewController:
class HomeViewController {
}
//....
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let bookDetailVc = self.storyboard?.instantiateViewController(withIdentifier: "BookDetailView") as? BookDetailViewController
let item = items[indexPath.row]
bookDetailVc?.item = item
bookDetailVc?.delegate = self
self.navigationController!.pushViewController(bookDetailVc!, animated: true)
}
//.....
extension HomeViewController: BookDetailDelegate {
func updatePage(for bookItem: BookItem) {
let index = item.firstIndex(where: {$0.id = bookItem.id}) //here you should have a UUID or something
item[index] = bookItem
tableView.performBatchUpdates({
self.tableView.reloadRows(at: IndexPath(row: index, section: 0, with: .none)}
}, completion: nil))
}

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

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
}

Cell is not selectable embed UItableView inside main View?

I have UITableView embed inside a main UIView. My problem is when I click on a cell to select items, it is not responding to the selection or triggering the segue.
Note: In attributes inspecter
is selection supposed to be set to single selection?
Update Note: the prepareForSegue function is not triggering or print "test".
import UIKit
import SwiftValidator
import CoreData
class EditNpFormViewController: UIViewController,UITextFieldDelegate, UIViewControllerTransitioningDelegate{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(EditNpFormViewController.dismissKeybored)))
self.npVisitViewTable.dataSource = self
self.npVisitViewTable.delegate = self
loadValidaValidationSettings()
loadUIObjectsSetting()
populateUIobjects()
}
func loadValidaValidationSettings() {
validator.styleTransformers(success:{ (validationRule) -> Void in
// clear error label
validationRule.errorLabel?.hidden = true
validationRule.errorLabel?.text = ""
if let textField = validationRule.field as? UITextField {
textField.layer.borderColor = UIColor.greenColor().CGColor
textField.layer.borderWidth = 0.5
}
}, error:{ (validationError) -> Void in
print("error")
validationError.errorLabel?.hidden = false
validationError.errorLabel?.text = validationError.errorMessage
if let textField = validationError.field as? UITextField {
textField.layer.borderColor = UIColor.redColor().CGColor
textField.layer.borderWidth = 1.0
}
})
validator.registerField(firstNameTextField, errorLabel:firstNameErrorLabel , rules: [RequiredRule(), AlphaRule()])
validator.registerField(lastNameTextField, errorLabel:lastNameErrorLabel , rules: [RequiredRule(), AlphaRule()])
validator.registerField(brnTextFieled, errorLabel:brnErrorLabel, rules: [RequiredRule(), AlphaNumericRule()])
}
func loadUIObjectsSetting(){
self.firstNameTextField.delegate = self
self.lastNameTextField.delegate = self
self.brnTextFieled.delegate = self
self.pickerLhsc.dataSource = self
self.pickerLhsc.delegate = self
self.pickerLhsc.tag = 0
self.ltchTextFieled.inputView = pickerLhsc
self.ltchTextFieled.delegate = self
self.ltchTextFieled.hidden = true
}
func populateUIobjects(){
self.firstNameTextField.text = selectedNpForm!.patientFirstName
self.lastNameTextField.text = selectedNpForm!.patientLastName
self.brnTextFieled.text = selectedNpForm!.brnNumber
self.ltchTextFieled.text = pickerDataLtch.filter { $0.uniqId == selectedNpForm!.ltch }.first?.hospital ?? ""
self.isPatientLtchResidentSwitch.setOn((selectedNpForm!.isPatientLtchResident == 0 ?false : true), animated: true)
self.ltchTextFieled.hidden = !(isPatientLtchResidentSwitch.on ? true : false)
reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: - Methods
func dismissKeybored(){
firstNameTextField.resignFirstResponder()
lastNameTextField.resignFirstResponder()
brnTextFieled.resignFirstResponder()
ltchTextFieled.resignFirstResponder()
}
func textFieledShouldReturn(){
firstNameTextField.resignFirstResponder()
lastNameTextField.resignFirstResponder()
brnTextFieled.resignFirstResponder()
ltchTextFieled.resignFirstResponder()
}
func hideKeyboard(){
self.view.endEditing(true)
}
func validationSuccessful() {
print("Validation Success!")
}
func validationFailed(errors:[(Validatable, ValidationError)]) {
print("Validation FAILED!")
// turn the fields to red
for (field, error) in errors {
if let field = field as? UITextField {
field.layer.borderColor = UIColor.redColor().CGColor
field.layer.borderWidth = 1.0
}
error.errorLabel?.text = error.errorMessage // works if you added labels
error.errorLabel?.hidden = false
}
}
func reloadData(predicate: NSPredicate? = nil) {
if let selectedNpForm = selectedNpForm {
if let formVisits = selectedNpForm.visits?.allObjects {
npVisits = formVisits as! [NpVisit]
}
} else {
let fetchRequest = NSFetchRequest(entityName: "NpVisit")
fetchRequest.predicate = predicate
do {
if let results = try moc.executeFetchRequest(fetchRequest) as? [NpVisit] {
npVisits = results
}
} catch {
fatalError("There was an error fetching the list of devices!")
}
}
npVisitViewTable.reloadData()
}
//MARK: - #IBAction
#IBAction func EditBrn(sender: AnyObject) {
print("Validating...")
//validator.validate(self)
}
#IBAction func saveNpForm(sender: AnyObject) {
if let selectedNpFormId = moc.objectWithID(selectedNpForm!.objectID) as? NpForm{
selectedNpFormId.brnNumber = brnTextFieled.text
selectedNpFormId.patientFirstName = firstNameTextField.text
selectedNpFormId.patientLastName = lastNameTextField.text
selectedNpFormId.isPatientLtchResident = isPatientLtchResidentSwitch.on ? true : false
selectedNpFormId.ltch = hospitalUniqId
}
do {
try moc.save()
} catch let error as NSError {
print("Could not save \(error), \(error)")
}
}
#IBAction func dosePatientResideLtch(sender: AnyObject) {
self.ltchTextFieled.hidden = !(isPatientLtchResidentSwitch.on ? true : false)
}
// MARK: Validate single field
// Don't forget to use UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
validator.validateField(textField){ error in
if error == nil {
// Field validation was successful
} else {
// Validation error occurred
}
}
return true
}
//MARK: - Segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "addNewNoVisit"){
if let distination = segue.destinationViewController as? MapViewController{
distination.clientNpForm._patientFirstName = firstNameTextField.text!
distination.clientNpForm._patientLastName = lastNameTextField.text!
distination.clientNpForm._brnNumber = brnTextFieled.text!
distination.clientNpForm._isPatientLtchResident = isPatientLtchResidentSwitch.on ? true : false
distination.clientNpForm._ltch = hospitalUniqId
distination.editNpFormMode = editNpFormMode
distination.selectedNpForm = selectedNpForm
}
}
if (segue.identifier == "sendNpVisitToVisitDetailSegue"){
print("test segue fired ")
}
}
//MARK: - #IBOutlet
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var brnTextFieled: UITextField!
#IBOutlet weak var npVisitViewTable: UITableView!
#IBOutlet weak var firstNameErrorLabel: UILabel!
#IBOutlet weak var lastNameErrorLabel: UILabel!
#IBOutlet weak var brnErrorLabel: UILabel!
#IBOutlet weak var isPatientLtchResidentSwitch: UISwitch!
#IBOutlet weak var ltchTextFieled: UITextField!
#IBOutlet weak var saveNpForm: UIButton!
//MARK: -VARIABLES
let validator = Validator()
var pickerLhsc = UIPickerView()
var editNpFormMode = FormMode.Edit
var selectedNpForm = NpForm?()
var npVisits = [NpVisit]()
var moc = DataController().managedObjectContext
var hospitalUniqId:Int = 0
var pickerDataLtch:[(uniqId:Int,hospital:String)] = [(229,"hospital 1"),
(230,"hospital 2"),
(231,"hospital 3")]
}
extension EditNpFormViewController:UIPickerViewDataSource,UIPickerViewDelegate {
// MARK, - Picker View
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
//return pickerDataLhsc.count
return pickerDataLtch.count
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
hospitalUniqId = pickerDataLtch[row].uniqId
ltchTextFieled.text = pickerDataLtch[row].hospital
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)-> String? {
return pickerDataLtch[row].hospital
}
}
extension EditNpFormViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(npVisitViewTable: UITableView, numberOfRowsInSection section: Int) -> Int{
return npVisits.count
}
func tableView(npVisitViewTable: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell:UITableViewCell = npVisitViewTable.dequeueReusableCellWithIdentifier("visitCell")! as UITableViewCell
cell.textLabel?.text = "Visit #\(indexPath.row + 1)"
return cell
}
func tableView(npVisitViewTable: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(npVisitViewTable: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("selected row ")
}
}
In this answer I am making the assumption that didSelectRowAtIndexPath is firing.
In your didSelectRowAtIndexPath, you need to fire the segue that you created from the cell to the second view controller:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("selected row", indexPath)
performSegue(with: "segueIdentifier")
}
I'm not sure what version of Swift you are using, so you might need to make some slight changes.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//self.performSegue(withIdentifier: "sites", sender: self)
self.performSegue(withIdentifier: "CHANGE-TO-YOUT-SEGUE-ID", sender: self)
}
is the "hello" get printed? if so, call your segue from this point. Don't forget to connect your segue (from the top left square) in the story board as manual segue:
If func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) is not called at all, your problem is that the delegate of your UITableView is not set (or not correctly set). The delegate should be your EditNpFormViewController.
You can easily set the delegate of your UITableView instance by simply Control-dragging from the table to the controller that contains it (inside the Storyboard). The other simple way is by setting
tableView.delegate = self
You can just select the table inside the storyboard and drag the delegate from the right panel's circle to your controller:
If your delegate is set right, check the selection attribute of the cell itself:
If your table view is in editing mode, you need to set tableView.allowsSelectionDuringEditing = true

Resources