Populating UI with info from tableview cell? - Swift - swift

Hi I have tried to follow tutorials online but can't seem to get it working.
I am trying to populate a view controller with information from the pressed cell from a table view ... it picks up which cell has been selected but then opens the view controller twice... it also doesn't populate the ui label?
Please advise thank you.
Table View Class where user selects a cell ;
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
title = currentCell.textLabel?.text
performSegue(withIdentifier: "passInfoSegue", sender: self)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "passInfoSegue") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! NextViewController
// your new view controller should have property that will store passed value
viewController.passedValue = title!
}
}
Next View Controller ;
class NextViewController : UIViewController{
var passedValue : String!
#IBOutlet weak var featLabel: UITextView!
#IBOutlet weak var descripLabel: UITextView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var ampImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
featLabel.text = passedValue
}
}

Related

Table Content disappears on Scroll in TableView with Custom Cell using Subview - Swift

I have a ViewController which uses multiple Subviews (HomeViewController, etc.) which can be selected via a Custom Tab Bar at the bottom of my app. Inside the HomeViewController there is a UIView containing a UITableView containing a Prototype Custom Cell with name and image.
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var friendView: UITableView!
let friends = ["batman", "harsh", "ava", "sasha", "fatima", "alfred"]
override func viewDidLoad() {
super.viewDidLoad()
friendView.delegate = self
friendView.dataSource = self
friendView.allowsSelection = false
}
}
extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friends.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = friendView.dequeueReusableCell(withIdentifier: "customCell") as! CustomCell
let friend = friends[indexPath.row]
cell.avatarImg.image = UIImage(named: friend)
cell.nameLbl.text = friend
return cell
}
}
Custom cell:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var friendView: UIView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var avatarImg: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
When I start the app, everything looks just fine. However, when I start scrolling inside the table, all data suddenly disappears. All relations between storyboard and code should be just fine. I think it might have got something to do with my need of using a Subview.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tabBarView: UIView!
#IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
Design.makeCornersRound(view: tabBarView, radius: 10.0)
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
self.switchToHomeViewController()
}
}
#IBAction func onClickTabBar(_ sender: UIButton) {
let tag = sender.tag
if tag == 1 {
switchToIncomingsViewController()
}
else if tag == 2 {
switchToSpendingsViewController()
}
else if tag == 3 {
switchToHomeViewController()
}
else if tag == 4 {
switchToSavingsViewController()
}
else if tag == 5 {
switchToSettingsViewController()
}
}
func switchToHomeViewController() {
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
contentView.addSubview(Home.view)
Home.didMove(toParent: self)
}
...
}
Reference to the tutorial I have been trying to implement: https://www.youtube.com/watch?v=ON3Z0PXSoVk
In this function:
func switchToHomeViewController() {
// 1
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// 2
contentView.addSubview(Home.view)
// 3
Home.didMove(toParent: self)
// 4
}
At 1 you create an instance of HomeViewController
at 2 you add its view to cotentView
at 3 you call didMove() ... but that doesn't do anything because you haven't added the controller to your hierarchy
at 4 your Home instance goes away, so the code in that controller no longer exists
You need to add the controller as a child controller.
As a side note, use lowerCase for variable names:
func switchToHomeViewController() {
// create an instance of HomeViewController
guard let homeVC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// add it as a child view controller
self.addChild(homeVC)
// add its view
contentView.addSubview(homeVC.view)
// here you should either set the view's frame or add constraints
// such as:
homeVC.view.frame = contentView.bounds
// inform the controller that it moved to a parent controller
homeVC.didMove(toParent: self)
}

passing CoreData from UITableViewCell to viewcontroller

I'm trying to create the feature where when a user clicks on a specific cell in my UITableView, the Project will segue to a new ViewController and show all the information that has been saved in the CoreData. The Problem is that when I touch on a cell I get an error of
Unexpected nil while unwrapping optional value
Here is my code as it is right now within the ViewController that has the TableView
class ContactViewController: UIViewController, UITableViewDataSource, NSFetchedResultsControllerDelegate, UITableViewDelegate {
var selectName:String?
var selectPhone:String?
var selectComment:String?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectName = contact[indexPath.row].name
selectPhone = contact[indexPath.row].phone
selectComment = contact[indexPath.row].comments
performSegue(withIdentifier: "MySegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MySegue"{
if let destVC = segue.destination as? IndiViewController {
destVC.NameLabel = selectName
destVC.PhoneLabel = selectPhone
destVC.CommentLabel = selectComment
}
}
}
This is my code in IndiViewController (the VC in which I want the user to view the contact)
class IndiViewController: UIViewController {
#IBOutlet var NameLabel: UILabel!
#IBOutlet var PhoneLabel: UILabel!
#IBOutlet var CommentsLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
I've tried a few other methods but all still delivered the same error
I troubleshooted a bit to see which variable truly was causing the nil by doing this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MySegue"{
if let destVC = segue.destination as? IndiViewController {
destVC.NameLabel = "selectName" //I thought if perhaps the nil was the var selectName this would at least let me know
}
}
However even through this, the app crashes and gives the same error. So I think the issue is with the Labels in the IndiViewController.
So I tried creating an empty String and assigning it to NameLabel like this:
class IndiViewController: UIViewController {
#IBOutlet var NameLabel: UILabel!
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
NameLabel.text = name
}
}
but still no luck.
What am I doing wrong?
Yet it can be an IBOutlet error, even for typo.
delete #IBOutlet var NameLabel: UILabel!
remove the link in the StoryBoard "referencing outlets" tab
click-drag a new outlet for the UILabel and do not name it with a capital letter
// #IBOutlet var NameLabel: UILabel!
#IBOutlet var nameLabel: UILabel!
Edit
Also try to pass your informations in two times :
create a new var in your destinationViewController like :
class IndiViewController: UIViewController {
#IBOutlet var nameLabel: UILabel!
var myText: String = ""
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = myText // assign the var to the labels text
}
}
In your prepareForSegue method assign myText and not the nameLabel.text
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MySegue"{
if let destVC = segue.destination as? IndiViewController {
destVC.myText = "selectName"
}
}
}
my guess is that you cant acces IBOutlets in prepare(for segue:) method. The destination ViewController is not built yet

Trouble with segue/unsegue when making a list app

I'm new to coding, so please bear with me. I was following an online tutorial that worked with plists to make a habit list app. I have a table view controller that shows a list of habits and a segue that presents modally a view controller that has text fields to add a habit.
enter image description here
Every time it runs, nothing happens when I click on the "save" and "cancel" buttons. I realize this is a vague question as it doesn't pinpoint to a specific issue, but I am really struggling with fixing this issue and would really appreciate if someone proofreads the code. The app builds and runs with no warnings.
This is the table view controller that shows the habits:
class TableViewController: UITableViewController {
//MARK: Properties
var habits = [Habit]()
//MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return habits.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {
fatalError("The dequeued cell is not an instance of ViewController.")
}
// Fetches the appropriate habit for the data source layout.
let habit = habits[indexPath.row]
cell.textLabel?.text = habit.mainGoal
cell.detailTextLabel?.text = habit.microGoal
return cell
}
#IBAction func unwindToHabitList(sender: UIStoryboardSegue) {
if let source = sender.source as?ViewController, let habit = source.habit {
//add a new habit
let newIndexPath = IndexPath(row: habits.count, section: 0)
habits.append(habit)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
This is the view controller that adds a habit:
class ViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
#IBOutlet weak var saveButton: UIBarButtonItem!
#IBOutlet weak var mainGoalTextField: UITextField!
#IBOutlet weak var microGoalTextField: UITextField!
var habit: Habit?
//method for configuring controller before presenting
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
//configure this destination view controller only when save button is pressed
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
let mainGoal = mainGoalTextField.text ?? ""
let microGoal = microGoalTextField.text ?? ""
//set the habit to be passed on to tableViewController after the unwind segue
habit = Habit(mainGoal: mainGoal, microGoal: microGoal)
}
#IBAction func cancel(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
let isPresentingInAddHabitMode = presentingViewController is UINavigationController
if isPresentingInAddHabitMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController{
owningNavigationController.popViewController(animated: true)
}
else {
fatalError("The ViewController is not inside a navigation controller.")
}
}
I appreciate any and all help in advance!
STORYBOARD CONNECTIONS:
TABLEVIEW CONTROLLER CONNECTIONS
ADD HABIT VIEW CONTROLLER CONNECTIONS

Passing a variable from a customCell.xib's button to another UIViewController

My custom cell has a button that when clicked, the user can be taken to another ViewControler. That button has a titleLabel with the String of a user id. What I want to do is take the user to a new UIViewController, passing that clicked buttons's titleLabel (user id) to a variable on the new View Controller. That way, I can use that variable (user id) get further information from firebase and display it on the UI View controller.
on the .xib of the custom cell, I print the UID to make sure each button prints with the correspondent ID, which it does. I can't figure out a way to pass that ID to a new ViewController.
I tried researching online and I found out you can't do prepare(for segue) or performsegue(WithIdentifier) on a customCell.xib.
I tried doing delegates and then protocols but still couldn't get it to work. I am new with Swift. Any help would be great, thank you!
This is the customCell.Xib's Swift file:
class CustomCellTableViewCell: UITableViewCell {
#IBOutlet weak var postImage: UIImageView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var descriptionLbl: UITextView!
#IBOutlet weak var profileImageBtn: UIButton!
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var view: UIView!
var btnSelected : Bool = false
var vcInstance: ProfilesTableVC?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
view.clipsToBounds = true
view.layer.cornerRadius = view.frame.size.width / 2
profileImage.layer.cornerRadius = profileImage.frame.size.width / 2
descriptionLbl.alpha = 0.7
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func profileBtnPressed(_ sender: Any) {
let passValue = UserProfileVC()
let profileTvC = ProfilesTableVC()
print (profileImageBtn.titleLabel!.text!)
var id = (profileImageBtn.titleLabel!.text!)
profileTvC.didSelectProfileBtn(senderID: id)
}
This is the tableViewController, where I everything gets loaded (not where I want to pass the value). I tried passing the value here and then do a prepareForSegue to pass the value to the new UIViewController but the value becomes nil after the segue happens. I am just including code where the .xib call the function from the table view.
class ProfilesTableVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate,UINavigationControllerDelegate, UIImagePickerControllerDelegate{
let cell = CustomCellTableViewCell()
func didSelectProfileBtn(senderID: String) {
senderIDArray.append(senderID)
var index = senderIDArray.count - 1
selectedCellUserID = senderIDArray[index]
performSegue(withIdentifier: "showSendersProfile", sender: Any?.self)
}
This is the UIViewController where I want to pass the variable and display further information from Firebase using that ID
import UIKit
import Firebase
class UserProfileVC: UIViewController {
let customCell = CustomCellTableViewCell()
let profileTvC = ProfilesTableVC()
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var nameLbl: UILabel!
var delegate : Any?
var getName = String()
var getsenderID = String()
let userDataRef = Database.database().reference().child("Users")
override func viewDidLoad() {
super.viewDidLoad()
print("ID is: \(profileTvC.selectedCellUserID)")
let sender = getsenderID
print(sender)
}
}
If you don't want to use protocols, you can addTarget to the button in the tableView - cellForRowAt method, also in that method you set the tag to the row index value.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let profileCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! MoviewReviewCell
reviewCell.profileImageBtn.tag = indexPath.row
reviewCell.profileImageBtn.addTarget(self, action: #selector(tappedOnXibCellButton), for: .touchUpInside)
return reviewCell
}
#objc func tappedOnXibCellButton(sender: UIButton) {
print(sender.tag)
performSegue(withIdentifier: reviewListToDetails, sender: sender.tag)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let identifier = segue.identifier
if identifier == "segueName" {
let destViewController = segue.destination as! DestinationClassViewController
let selectedIndex = sender as! Int
let profileId = profilesList[selectedIndex].id
destViewController.profileId = profileId
}
}
You seem to create a NEW ProfilesTableVC, and try to perform the segue on. The newly created one is not on your view stack, and not from your storyboard.
You could add this while returned cellForRowAt (at the ProfilesTableVC of course):
cell.vcInstance = self
Then in the button click
#IBAction func profileBtnPressed(_ sender: Any) {
print (profileImageBtn.titleLabel!.text!)
var id = (profileImageBtn.titleLabel!.text!)
vcInstance?.didSelectProfileBtn(senderID: id)
}
You can do it with protocols/delegates. I think you tried but there is something wrong with your trial.
Define a callback delegate:
protocol CustomCellDelegate: AnyObject {
func customCell(didSelectButton name: String)
}
You will notice extending AnyObject. This is to allow weak references, try to read about Swift ARC
Then, in your cell:
weak var delegate: CustomCellDelegate?
Then in the profileBtnPressed(_ sender: Any)
#IBAction func profileBtnPressed(_ sender: Any) {
var id = (profileImageBtn.titleLabel!.text!)
delegate?.customCell(didSelectButton: id)
}
When dequeing the cell:
(cell as? CustomCellTableViewCell)?.delegate = self

Populate tableview using contacts framework

I want to populate a tableview with contact details that I have entered in the app.
first i have a table view controller that will show the list of entered contacts. its swift file looks like this.
import Contacts
import ContactsUI
class thecontacts: UITableViewController, CNContactPickerDelegate, UIPickerViewDelegate {
let contacts = CNMutableContact()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell!
return cell
}
I want to populate this from another view controller. I want to populate this tableview with a persons name, and have it as a contact in the tableview. This view controllers swift file is:
import Contacts
class newContact: UIViewController {
//outlets
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
var tableViewController: newContact? = nil
override func viewDidLoad() {
}
#IBAction func addBuddy(sender: AnyObject) {
let contacts = CNMutableContact()
contacts.nickname = nameTextField!.text!;
contacts.emailAddresses = [CNLabeledValue(label: nil, value: emailTextField.text!)]
self.tableViewController?.addBuddy(contacts)
dismiss()
}
func dismiss(){
dismissViewControllerAnimated(true, completion: nil)
}
}
my tableViewController file is called thecontacts.swift. My new contact details viewController is called newContacts.swift.
the problem I have is that the details I enter into the newContact file are not appearing in my thecontacts table view.