How to update UITableView when a CustomCell label value changes? - swift

I have a custom cell class with two buttons and one label defined in its own class. Am using protocol-delegates to update the value of the label when the button is pressed. But am not able to figure out how to update the UITableView.
protocol customCellDelegate: class {
func didTapDecrementBtn(_ sender: AllCountersTableViewCell)
func didTapIncrementBtn(_ sender: AllCountersTableViewCell)
func updateTableViewCell(_ sender: AllCountersTableViewCell)
}
class AllCountersTableViewCell: UITableViewCell {
#IBOutlet weak var counterValueLbl: UILabel!
#IBOutlet weak var counterNameLbl: UILabel!
#IBOutlet weak var counterResetDateLbl: UILabel!
#IBOutlet weak var counterDecrementBtn: UIButton!
#IBOutlet weak var counterIncrementBtn: UIButton!
weak var delegate: customCellDelegate?
#IBAction func decrementBtnPressed(_ sender: UIButton) {
delegate?.didTapDecrementBtn(self)
delegate?.updateTableViewCell(self)
}
#IBAction func incrementBtnPressed(_ sender: UIButton) {
delegate?.didTapIncrementBtn(self)
delegate?.updateTableViewCell(self)
}
In my ViewController, I have provided the delegate. But reloadData() is not working, so although sender.counterLbl.value is changing its not reflecting on the view.
extension AllCountersVC: customCellDelegate {
func didTapIncrementBtn(_ sender: AllCountersTableViewCell) {
guard let tappedCellIndexPath = tableView.indexPath(for: sender) else {return}
let rowNoOfCustomCell = tappedCellIndexPath[1]
let newValue = String(allCounter[rowNoOfCustomCell].incrementCounterValue(by: allCounter[rowNoOfCustomCell].counterIncrementValue))
sender.counterValueLbl.text = newValue
}
func updateTableViewCell(_ sender: AllCountersTableViewCell) {
allCountersTableView.reloadData()
}

In cellForRow you must set cell.delegate = self for this logic to start working. Its not enough just to make you controller confroms to your custom cell delegate protocol. From your post I assume delegate is always nil in the cell, that is why it does not work.

Related

Swift transit from vc to another vc

ViewController has a label and a button for go to secondVC. And secondVC has a text field , label , button to write user-entered text in the text field on the label. When user press the back button of navigation bar, I want transit secondVC's label's text to ViewController's label's text. How can I do this ?
ViewController's code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func goVC2(_ sender: Any) {
performSegue(withIdentifier: "toVC2", sender: nil)
}
}
secondVC's code:
import UIKit
class secondVC: UIViewController {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var resultLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveClicked(_ sender: Any) {
resultLabel.text = nameField.text
}
}
I tried prepare for segue in ViewController and but it was error. And I searched on google for this but I couldn't find solution.
Use protocol for pass data from second VC to first VC. you need to create your own delegate method and call on dismiss with pass data, check below code :----
ViewController's code:
import UIKit
class ViewController: UIViewController, Delegate {
#IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func save(str : String) {
self.nameLabel.text = str
}
#IBAction func goVC2(_ sender: Any) {
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondVC") as? secondVC {
vc.delegate = self
self.present(vc, animated: true, completion: nil)
}
}
}
secondVC's code:
import UIKit
protocol Delegate : AnyObject {
func save(str : String)
}
class secondVC: UIViewController {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var resultLabel: UILabel!
weak var delegate : Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveClicked(_ sender: Any) {
if let del = self.delegate {
let txt = nameField.text
del.save(str: txt)
self.dismiss(animated: true, completion: nil)
}
}
}

How can I properly send values between classes in Swift?

I've been working on a text based adventure game using Swift. However, I can't seem to change the default values for specific classes.
Below is the code for the class that allows me to select my player class
import UIKit
class ClassSelectionController: UIViewController
{
//Default class values
var character = (0, 0, "", 0)
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//class button actions
#IBAction func fighterBtn(_ sender: Any)
{
character = (50, 60, "Steal Sword", 18)
performSegue(withIdentifier: "Character", sender: self)
}
#IBAction func wizerdBtn(_ sender: Any)
{
character = (25, 70, "Staff", 15)
performSegue(withIdentifier: "Character", sender: self)
}
#IBAction func thiefBtn(_ sender: Any)
{
character = (30, 60, "Dagger", 18)
performSegue(withIdentifier: "Character", sender: self)
}
#IBAction func archerBtn(_ sender: Any)
{
character = (50, 60, "Bow & Arrow", 16)
performSegue(withIdentifier: "Character", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: (Any)?)
{
//code for segue
var vc = segue.destination as! ViewController
vc.finalCharacter = self.character
}
}
And this is the class that receives the data for the player class and displays it.
import UIKit
class ViewController: UIViewController
{
var finalCharacter = (0, 0, "", 0)
//****************************************
//Setup for outlets go between these lines
//****************************************
#IBOutlet weak var healthLabel: UILabel!
#IBOutlet weak var damageLabel: UILabel!
#IBOutlet weak var weaponLabel: UILabel!
#IBOutlet weak var armorLabel: UILabel!
#IBOutlet weak var storyLabel: UILabel!
#IBOutlet weak var actionButton: UIButton!
#IBOutlet weak var northBtn: UIButton!
#IBOutlet weak var southBtn: UIButton!
#IBOutlet weak var eastBtn: UIButton!
#IBOutlet weak var westBtn: UIButton!
//****************************************
//Setup for outlets go between these lines
//****************************************
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
healthLabel.text = "Health: \(finalCharacter.0)"
damageLabel.text = "Damage: \(finalCharacter.1)"
weaponLabel.text = "Weapon: " + finalCharacter.2
armorLabel.text = "Armor : \(finalCharacter.3)"
}
//****************************************
//Setup for buttons go between these lines
//****************************************
#IBAction func actionButton(_ sender: Any)
{
}
#IBAction func northButton(_ sender: Any)
{
}
#IBAction func southButton(_ sender: Any)
{
}
#IBAction func eastButton(_ sender: Any)
{
}
#IBAction func westButton(_ sender: Any)
{
}
//****************************************
//Setup for buttons go between these lines
//****************************************
}
Even though my code does not currently show it I did put print values in the class buttons just so see if the values have changed when I select a class and I have seen that they do change when the button is pressed. I also poked around in ViewController and changed the finalCharacter values, just to see if that affected anything, which it didn't. So my educated guess is that the problem has to be in ClassSelectionController.
So the main problem is that I will click on the wizard player class, and I'd expect the class stats for the wizard to pop up (I.e. 25, 70, "Staff", 15), but I'll only get the default values that are in the ClassSelectionController (0, 0, "", 0).
What is happening here that you have multiple UIStoryboardSegue named "Character" linked with every button
so what happens is when you press the button the Segue is called before the Action button and in addition the UIStoryboardSegue is called again (if you place a debugger in the viewDidLoad you would see that it goes there two times).
Solution
Remove all the UIStoryboardSegue linked from the buttons
Make a new UIStoryboardSegue from the ClassSelectionController to the next ViewController name it 'Character'
You dont need to change anything from the code but should add a safe check
override func prepare(for segue: UIStoryboardSegue, sender: (Any)?)
{
if (segue.identifier == "Character") {
let vc = segue.destination as! ViewController
vc.finalCharacter = self.character
}
}

Unable to perform segue from UIView connected to XIB

This is UIView file's owner of XIB
import UIKit
class sideBarContent: UIView {
#IBOutlet weak var userName: UILabel!
#IBOutlet var sideBarContentView: UIView!
let mapviewcontroller = MapViewController()
#IBAction func userProfile(_ sender: UIButton) {
mapviewcontroller.performSegueFromView(stringFromView: "userprofile")
}
Whereas inside my UIviewcontroller
class MapViewController: UIViewController
{
func performSegueFromView(stringFromView:String){
performSegue(withIdentifier: "\(stringFromView)", sender: nil)
}
}
It says that my segue identifier does not exist, please help, i'm quite lost. And i couldn't perform segue in UIView as well, and i tried to use storyboard ID method too, but the present(vc,animated:true,completion:nil) not showing up.
First things first, I think you should not access the view controller from your view. It is better to model the controller logic in the view controller rather than in the view.
That said, you can use a delegate pattern to implement your functionality.
import UIKit
class SideBarContent: UIView {
#IBOutlet weak var userName: UILabel!
#IBOutlet var sideBarContentView: UIView!
let delegate: SideBarContentDelegate? = nil
#IBAction func userProfile(_ sender: UIButton) {
self.delegate?.performSegueFromView(withIdentifier: "userprofile")
}
}
protocol SideBarContentDelegate {
func performSegueFromView(withIdentifier identifier: String)
}
class MapViewController: UIViewController, SideBarContentDelegate {
#IBOutlet private weak var sidebarView: SideBarContent!
override func viewDidLoad() {
super.viewDidLoad()
self.sidebarView.delegate = self
}
func performSegueFromView(withIdentifier identifier: String) {
self.performSegue(withIdentifier: identifier, sender: nil)
}
}
More info : https://stackoverflow.com/a/1340470/2603900

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

How to use UIButtons in a Cell with Functions?

I created a custom cell added the actions and outlets.
I want to be able to get the cell indexpath when the cell button is pressed.
I am send data to my parse server and I need to the cell indexPath.row so when someone clicks the button the count for likes or dislikes goes up.
My Custom Cell :
class myTVC: UITableViewCell {
#IBOutlet weak var greenUp: UIButton!
#IBOutlet weak var redDown: UIButton!
#IBOutlet weak var imageViewTVC: UIImageView!
#IBOutlet weak var upVote: UILabel!
#IBOutlet weak var downVote: UILabel!
#IBOutlet weak var saveButton: UIButton!
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
}
#IBAction func voteDownPressed(sender: UIButton) {
print("downVote")
}
#IBAction func voteUpPressed(sender: UIButton) {
print("upVote")
}
}