Delegate used to control a button inside a cell not working - swift

I have a custom UIView with 3 tableviews, the tableviews are self-sizing depending on the content they have, and they a show a different nib when they are empty than when they have content. All of this is embedded in a scroll view and it's working already, my view scrolls and the tableviews display the content appropriately and they autosize properly, the scroll view's size is adjusting without problems either.
In two of the tableviews when the tableviews have items there are some buttons in the cell nib that I want to access along with the index of the cell clicked inside the View controller where I have defined the Table view.
Here in the pic I'm showing the tableViews each with one item, the Canastas tableView has just a delete item button and the Productos tableView has a delete item button and a stepper to increase or decrease the amount.
I found a solution involving delegates in StackOverflow
Get button click inside UI table view cell, which I implemented. But for some reason it isn't working and the code in the delegate method isn't being executed.
Here is my code:
CanastasViewCell
import UIKit
protocol CanastasViewCellDelegate: class {
func closeButtonTapped(at index: IndexPath)
}
class CanastasViewCell: UITableViewCell {
#IBOutlet weak var imagenProducto: UIImageView!
#IBOutlet weak var nombreProducto: UILabel!
#IBOutlet weak var descProducto: UILabel!
#IBOutlet weak var precioProducto: UILabel!
#IBOutlet weak var closeButton: UIButton!
weak var delegate: CanastasViewCellDelegate?
var indexPath: IndexPath!
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func eliminarProducto(_ sender: AnyObject) {
self.delegate?.closeButtonTapped(at: indexPath)
}
}
CarritoViewController
import UIKit
class CarritoViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource, CanastasViewCellDelegate {
func closeButtonTapped(at index: IndexPath) {
print("Button tapped at index:\(index)")
}

It looks like you're not setting the delegate inside CarritoViewController. You'll want to make sure that you set cell.delegate = self inside func tableView(UITableView, cellForRowAt: IndexPath).

Related

How to connect UITableViewCell button to perform UITableView functions?

I have an app which utilizes a tableView which contains a prototype cell. This prototype cell contains a few labels and a button (connected to that cell's UITableViewCell file). If that's not clear, this is what I mean:
class ContactsCell: UITableViewCell { //this custom cell is used as the prototype cell for the tableView
#IBOutlet weak var firstNameLabel: UILabel!
#IBOutlet weak var lastNameLabel: UILabel!
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var indexPathRowLabel: UILabel!
#IBOutlet weak var replaceContactButtonUI: UIButton!
#IBAction func replaceContactButtonTapped(_ sender: Any) {
//THIS is the button in question
}
The reason for this is because I'd like each cell in the tableView to have all of those labels (and the button), but with each row having different values in the labels. These values are handled by the cellForRowAt tableView function, via a system as follows:
Pressing a button (not the button in the cells I was talking about... a different button that is at the top of the VC, outside of the tableView) brings up the contact picker:
func selectContact(){//allows user to bring up contactPicker
let contactPicker = CNContactPickerViewController()
contactPicker.displayedPropertyKeys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey]
contactPicker.delegate = self
contactPicker.predicateForEnablingContact = NSPredicate(format: "phoneNumbers.#count >= 0")
contactPicker.predicateForSelectionOfContact = NSPredicate(format: "phoneNumbers.#count >= 1")
self.present(contactPicker, animated: true, completion: nil)
}
#IBAction func selectContactsButton(_ sender: Any) {
selectContact()
//button brings up the picker to allow selection of contact
}
Selecting one of the contacts updates an array which is used by the tableView to update the value of the labels:
func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
contactArray.append(contactProperty.contact)
print(contactArray)// contactArray = [CNContact]()
contactsTableView.reloadData()// reloads the value of the labels
dismiss(animated: true)
}
These all work as intended. However, I run into a bit of an issue with the button that is in each cell. This button is intended to allow the replacement of a the CNContact in the tableView at the row of which the button was pressed... (ie if there are 4 rows, there are 4 "replace" buttons. Clicking the "replace" button of the third row replaces the CNContact at the 2nd index of the contactArray). This is where my issue is, though. Since the button, and its action, lives in the UITableViewCell's swift file, and not in the tableViewControllers, it doesn't have access to the contactArray to remove and append, nor the picker functions to allow the selection of the new contact, nor the tableView itself to run .reloadData(). How do I 'give access' to the UITableViewCell's swift file to utilize things from the tableView controller that it is a part of/connected to?
Note Its possible that the entire premise of this question is wrong and that I should've actually placed the button's action elsewhere (not in the UITableViewCell's swift file). If this is the case, where should I put it?
Since you have all the information in your ViewController, I would suggest moving your #IBAction into your ViewController as well. If you are worried about referencing the correct cell, you can set the tag value of your UIButton in cellForRow method. Then in #IBAction, you can use that value to access the correct index in the array as well as the cell itself
you can give selector to button in cellforrowatindexpath:
cell.replaceContactButtonUI.tag = index.row
cell.replaceContactButtonUI.addTarget(self, action: #selector(self.
selectContact(sender:)), for: .touchUpInside)
func selectContact(sender:UIButton){}
From button tag you can find the cell index by sender.tag.

Setting XIB inside custom cell tableView issue

I have followed this this tutorial
How to visualize reusable xibs in storyboards using IBDesignable
I have added a label inside the view to change the label and I'm calling the xib inside the custom cell for tableView. So I'm facing this issue,
fatal error: unexpectedly found nil while unwrapping an Optional value
While I’ve added this code inside the view and linked it
#IBOutlet weak var priceLabel: UILabel!
This is how I'm calling it inside the custom cell.
TableViewcell code:
class ProductListBigTableViewCell: UITableViewCell {
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var productQuantity: UILabel!
#IBOutlet weak var productAddButton: BaseButton!
#IBOutlet weak var prodcutPriceView: PriceView!
override func awakeFromNib() {
super.awakeFromNib()
productFirstName.text = "first product"
productFirstQuantity.text = "4 p"
prodcutPriceView.priceLabel.text = ""
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
I can figure that the view is not initialised yet so it is nil. I could not find a way or resource how to change a value inside XIB inside the UITableViewCell. So how I can change it and initial the view for the XIB?

How do I use UIStepper to change a property in a Realm Model Object?

I currently have a ViewController with a TableView inside it called SelectedListItemsViewController. This ViewController's TableView is populated by a custom cell class called SelectedListItemsTableViewCell.
I have an array of Realm Model Objects called selectedListItems, each of which has several properties. The SelectedListItemsTableViewCell populates the TableView with the listItem property of that indexPath.row's object, and each row has a UIStepper with a UILabel next to it that (as of now) shows UIStepper.value for each row. Ideally, the label will reflect the listItemWeight property of each row, and change it when incrementing or decrementing that row.
This is my custom cell:
class SelectedListItemsTableViewCell: UITableViewCell {
#IBOutlet var selectedListItemLabel: UILabel!
#IBOutlet weak var listItemWeightLabel: UILabel!
#IBOutlet weak var stepperControl: UIStepper!
#IBAction func stepperValueChanged(sender: UIStepper) {
listItemWeightLabel.text = Int(sender.value).description
}
}
And in my ViewController's cellForRowAtIndexPath, I've configured the cell like so:
// Configure the cell...
cell.selectedListItemLabel.text = selectedListItems[indexPath.row].listItem
cell.listItemWeightLabel.text = "\(selectedListItems[indexPath.row].listItemWeight)"
Which perfectly loads the listItem property, and the listItemWeight property shows up correctly, but as soon as I increment or decrement on the UIStepper it gets messed up.
How do I properly link my UILabel and UIStepper to the [indexPath.row].listItemWeight?
In the same method that gets called when your stepper updates, update your listItem. However, since this item is stored in your Realm database, you will have to get an instance of your Realm database and write the change to the database.
You can do this by having your TableViewCell hold on to an instance of the listItem. Your new TableViewCell class will look something like this:
class SelectedListItemsTableViewCell: UITableViewCell {
#IBOutlet var selectedListItemLabel: UILabel!
#IBOutlet weak var listItemWeightLabel: UILabel!
#IBOutlet weak var stepperControl: UIStepper!
var listItem: Item?
#IBAction func stepperValueChanged(sender: UIStepper) {
listItemWeightLabel.text = Int(sender.value).description
if let listItem = listItem {
let realm = try! Realm
try! realm.write {
listItem.listItemWeight = Int(sender.value) ?? 0
}
}
}
}
The above answer was helpful in leading me to the actual solution of my issue, but if anyone in the future is curious - I ended up using a closure.
In my custom cell class, I did
#IBAction func stepperValueChanged(sender: UIStepper) {
selectedListItemLabel.text = Int(sender.value).description
tapped?(self)
}
And in my view controller, I did
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SelectedListItemsTableViewCell
cell.stepperControl.value = selectedListItems[indexPath.row].listItemWeight
// Configure the cell...
cell.tapped = { (selectedCell) -> Void in
selectedListItems[indexPath.row].listItemWeight = cell.stepperControl.value
}
Which allowed me to access each cell's UIStepper in the view controller file.
It helped to read flashadvanced's Option 2 answer in this thread.

My scrollViewDidScroll function is not receiving calls

I'm trying to check if a user is scrolling up and down on my app but it's not calling my scrollViewDidScroll method
Any ideas why it's not printing received scroll when I scroll up and down on the app?
import UIKit
class CreateAccount: UIViewController, UITextFieldDelegate, UIScrollViewDelegate {
#IBOutlet weak var scrollViewer: UIScrollView!
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var reenterPasswordTextField: UITextField!
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.
}
func scrollViewDidScroll(scrollView: UIScrollView) {
print("received scroll")
}
}
Add the scrollview delegate. Whenever you implement a delegate method you need tell it what controller to use, usually it will be self. It's caught me out a few times.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
scrollViewer.delegate = self
}
You need to set delegate of scrollview also don't use tag to differentiate 2 scrollview instead of that create 2 outlets of scrollview and use that in UIScrollViewDelegate methods like this.
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView == scrollView1 {
print("received scrollview 1")
}
else if scrollView == scrollView2 {
print("received scrollview 2")
}
}
Delegate are given properly even if your scrollview delegate method not calling then please check you delegate method which you have added for scrollview, this may give warning due to using old swift version scrollview delegate with swift 4.0
Please correct it with below if this is the case:
Old Method:
func scrollViewDidScroll(scrollView: UIScrollView) {
}
New Method:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
}
You can set the .delegate = self as Sarcoma suggests, or:
CTRL drag from scrollView in storyboard onto the File Owner (it is located in the view hierarchy list, actually it is right by the yellow circle icon by the name of your controller: [O] Create Account and choose
delegate

Static Table View with dynamic text

I have a static table view, but I want the last cell to say "Logout, (username here)"...can I even do that with a static table view?
The language I am using is Swift.
So far, I've tried to use cellForRowAtIndexPath and only have it return anything when the indexPath.row is the last cell. That doesn't work. Furthermore, it changes all of the other cells and, on top of that, they lose their segues to the other views...
Yes, you can update controls in static cells in UITableViewController's UITableView. When you have a static table, you can just create IBOutlet references to the controls in the cells and update those outlets directly without use of any UITableViewDataSource methods. You can effectively just ignore the fact that it is a UITableView and treat these labels as if they were placed directly on the view in question. Just hook up the controls in the static cells to IBOutlet references in the view controller.
For example:
class ViewController: UITableViewController {
#IBOutlet weak var label1: UILabel! // in first cell
#IBOutlet weak var label2: UILabel! // in second cell
#IBOutlet weak var label3: UILabel! // in third cell
override func viewDidLoad() {
super.viewDidLoad()
label1.text = "foo"
label2.text = "bar"
label3.text = "baz"
}
}