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"
}
}
Related
Getting error Unexpectedly found nil while unwrapping an Optional value
while trying to pass data from tableviewcell to viewcontroller
DataCell.swift
#IBOutlet weak var containerView: UIView!
viewcontroller.swift
let dataCell = DataCell()
override func viewDidLoad() {
super.viewDidLoad()
dataCell.containerView.layer.cornerRadius = 10.0
}
#IBOutlet weak var containerView: UIView!
You have this IBOulet connection in a xib file or a in a stotryboard. And you are creating the cell programmatically in the view controller. When you create a cell programmatically its IBOutlet connections will be nil. Either created the containerView programmatically without IBOutlet in DataCell class or change the cornerRadius of the containerView in cellForRowAtIndexPath method.
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.
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).
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.
I want to add a cell in UICollectionCell on run time. What is the best way that not affected performances?
And It's my sample cell class:
class PersonViewCell: UICollectionViewCell {
#IBOutlet weak var coverImg: UIImageView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var titleLbl: UILabel!
var p: Person!
func configureCell(p: Person) {
self.titleLbl = p.title
nameLbl.text = p.name
coverImg.image = UIImage(named: "image-not-available")
}
}
Make a new instance variable in your class to store an array and save your JSON results to it. Then use that to return the number of cells in numberOfItemsInSection, and draw the cells in cellForItemAtIndexPath. If you want to update after that, just call collection.reloadData()
Also, assuming you're making your network request on a background thread, make sure you call collection.reloadData() on the main.