I have some data coming from the API and I am showing them in tableView cells, Each of those cell's have a "Download" button, I need to assign a fileID from API to the cell that the user has selected to Download.
I have already set up the button to a UITableViewCell class, I just need to have a idea how to make the button get the fileID depending on which cell is clicked since every cell has it's own fileID.
paymentsViewController class:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "paymentsCell", for: indexPath) as? paymentsModel else {return UITableViewCell()}
let currentInvoice = payments.Result![changeCustomerKey.DefaultsKeys.keyTwo].properties?[0].payments
let currentInvoices = currentInvoice?[indexPath.row]
cell.priceLabelPayments?.text = "€\(currentInvoices?.amount ?? 0.00)"
cell.whenDateLabel?.text = "\(currentInvoices?.insertionDate?.convertToDisplayForm() ?? "")"
cell.fromSubLabel.text = "\(currentInvoices?.bank ?? "")"
cell.downloadButtonPayments.layer.cornerRadius = CGFloat(5)
cell.localizePaymentsModel()
return cell
}
}
paymentsModel class:
class paymentsModel: UITableViewCell {
#IBOutlet weak var cellContentViewPayments: UIView!
#IBOutlet weak var downloadButtonPayments: UIButton!
#IBOutlet weak var priceLabelPayments: UILabel!
#IBOutlet weak var fromSubLabel: UILabel!
#IBOutlet weak var whenDateLabel: UILabel!
func localizePaymentsModel() {
let defaults = UserDefaults.standard
let lang = defaults.string(forKey: changeLanguageKey.DefaultsKeys.keyOne)
downloadButtonPayments.setTitle(NSLocalizedString("Download", tableName: nil, bundle: changeLanguage.createBundlePath(lang: lang ?? "sq" ), value: "", comment: ""), for: UIControl.State.normal)
}
}
You are thinking about this wrong. It isn't the cell that has the fileID. When the user taps on a cell or a button, you should ask the table view (or collection view) for the indexPath it currently occupies, then use the IndexPath to look up the model data for that entry.
See my answer here:
Thant link shows code that lets you figure out the IndexPath for any view in a table view cell. You could use that code from your button's IBAction.
Related
I am new to swift and have been experimenting with passing data between view controllers. I have been attempting to pass data from a view controller into a UITableViewCell, However once run my code has no effect.
DetailViewController
(passes data to the libraryViewController)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController: LibraryMovieViewController = segue.destination as! LibraryMovieViewController
DestViewController.movieTitle = movieTitle
DestViewController.movieRelease = movieReleaseDate
}
}
UITableViewCell
class MovieSearchTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var posterView: UIImageView!
#IBOutlet weak var overviewLabel: UILabel!
}
LibraryViewController
struct libMovie {
//let mainImage: UIImage
let title: String
let release: String
}
class LibraryMovieViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dataSource: [libMovie] = []
var movieTitle: String!
var movieRelease: String!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
// Do any additional setup after loading the view.
loadDataSource()
}
func loadDataSource(){
// dataSource.append(libMovie(title: " \(movieTitle)", release: " \(movieRelease)"))
}
}
extension LibraryMovieViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 115
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let movieCell = tableView.dequeueReusableCell(withIdentifier: "libCell", for: indexPath) as? LibraryMovieTableViewCell else {
return UITableViewCell()
}
let libMovie = dataSource[indexPath.row]
movieCell.cellTitleLabel.text = "Movie Title: \(movieTitle)"
movieCell.cellReleaseLabel.text = "Release Date: \(movieRelease)"
return movieCell
}
}
I would expect that when the app is run that movieTitle and movieReleaseDate are passed from the detail view controller and input into the library table cell, this is initiated by tapping a button on the detail view controller.
However this seems to have no affect on the program or simply returns blank cells.
No errors are reported in console nor does the app crash
In prepareForSegue you've passed data to movieTitle and movieRelease, while you're using dataSource to inflate data on tableView.
Either pass and object of [libMovie] to DestViewController.dataSource if you want to inflate multiple rows of different movies or return 1 in numberOfRowsInSection and pass set the label content in cell with movieTitle and movieRelease.
In your code, numberOfRowsInSection is returning dataSource.count, but doesn't seem to pass that data to this view controller.
You can either hardcode the numberOfRowsInSection as 1 or pass the dataSource from the first view controller and update your cellForRowAtIndexPath method.
The data source in the destination view controller is empty, what do you expect?
Uncomment the line in loadDataSource and reload the table view.
func loadDataSource(){
dataSource.append(ibMovie(title: movieTitle, release: movieRelease))
tableView.reloadData()
}
And replace
movieCell.cellTitleLabel.text = "Movie Title: \(movieTitle)"
movieCell.cellReleaseLabel.text = "Release Date: \(movieRelease)"
with
movieCell.cellTitleLabel.text = "Movie Title: \(libMovie.title)"
movieCell.cellReleaseLabel.text = "Release Date: \(libMovie.release)"
I am currently working on an app that allows users to input data into a form and the data will be stored in coredata locally. The data will be displayed in a table view in cells by admins. There are four types of data that has to be logged into a table cell. I tried using four labels in one cell to allow the data to be printed into the cell. Here is my table view right now.
This is some of my code right now.
The first part is on how the code extracts data and assignment to the four labels.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let person = people[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Table_VC_Cell
cell.nameLabel.text = person.value(forKey: "name") as? String
cell.placeLabel.text = person.value(forKey: "place") as? String
cell.contactnoLabel.text = person.value(forKey: "contactno") as? String
cell.purposeLabel.text = person.value(forKey: "purposeofvisit") as? String
return cell
}
}
This part is the connection of the labels and the code.
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var placeLabel: UILabel!
#IBOutlet weak var contactnoLabel: UILabel!
#IBOutlet weak var purposeLabel: UILabel!
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
}
}
Whenever I display the tables after inputting data in the form, the app crashes. Xcode specifically told me that the errors were due to this chunk of code. How should I go about fixing that?
I am trying to achieve functionality similar to Apple's reminders app where a tableview holds all the reminders and a + button at the end adds a new object.
My objects are held in an array called tempActions, which is the data source for the tableView.
Pressing 'Add Action' appends a new object to the array with the title "Empty Cell".
The title is a UITextView which users will be able to edit, but here's what I can't figure out how to do:
How do I take the text from the UITextView of that particular cell, append it to the array at the correct index (the index corresponds to indexPath.row) and then display it in the cell.label?
I thought of using the textViewDidEndEditing method but what I don't know how to do is reference the correct cell from the cellForRowAt method.
Would anyone be able to help clarify this, or am I approaching it in the wrong way?
Here's the code for the entire class:
class Step3: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate {
// Outlets
#IBOutlet weak var sectionText: UILabel!
#IBOutlet weak var sectionHeader: UILabel!
#IBOutlet weak var teableViewHeight: NSLayoutConstraint!
#IBOutlet weak var tableview: UITableView!
#IBAction func addAction(_ sender: Any) {
tempActions.append(Action(title: "Empty Cell", completed: false))
tableview.reloadData()
tableview.layoutIfNeeded()
teableViewHeight.constant = tableview.contentSize.height
print(tempActions)
}
#IBAction func nextAction(_ sender: Any) {
let newGoal = Goal(
title: tempTitle,
description: tempDescription,
duration: tempDuration,
actions: nil,
completed: false
)
newGoal.save()
performSegue(withIdentifier: "ToHome", sender: nil)
}
func textViewDidEndEditing(_ textView: UITextView) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tempActions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ActionCell", for: indexPath) as! ActionCell
cell.label.text = tempActions[indexPath.row].title
cell.label.textContainerInset = UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0);
cell.label.delegate = self
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableview.estimatedRowHeight = 40
tableview.rowHeight = UITableView.automaticDimension
}
}
Thanks in advance
If I understand it -- the textView is in a cell, and you want to find that cell in textViewDidEndEditing. If the superview of the textfield is the cell, you could do this:
func textViewDidEndEditing(_ textView: UITextView) {
if let cell = textView.superview as? ActionCell,
let indexPath = tableView.indexPath(for: cell) {
// Now you have the indexPath of the cell
// update tempActions
// YOUR CODE HERE
// Then reloadRows
tableView.reloadRows(at: [indexPath]), with: .automatic)
}
}
Another thing you could do is make tempAction's type have a unique ID and then store that in the ActionCell -- when you want to find the index, look up the ID in the tempActions array to find its index.
Another Swift beginner here. I simply want a Stepper in each of my TableView cells that increments a label in the same cell.
I have found a couple of questions on this topic, but they include other elements and I haven't been able to extract the basic concept.
Swift Stepper Action that changes UITextField and UILabel within same cell
Stepper on tableview cell (swift)
So far I have connected IBOutlets for my Label and Stepper, as well as an IBAction for my Stepper in my cell class.
class BuyStatsCell: UITableViewCell{
//these are working fine
#IBOutlet weak var category: UILabel!
#IBOutlet weak var average: UILabel!
#IBOutlet weak var price: UILabel!
//Outlet for Label and Stepper - How do I make these work?
#IBOutlet weak var purchaseAmount: UILabel!
#IBOutlet weak var addSubtract: UIStepper!
//Action for Stepper - And this?
#IBAction func stepperAction(_ sender: UIStepper) {
self.purchaseAmount.text = Int(sender.value).description
}
}
And I understand the concept of reusing the cell in the cellForRowAt indexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BuyStatsTabCell", for: indexPath) as! BuyStatsCell
cell.isUserInteractionEnabled = false
//these are working
cell.category.text = categories[indexPath.row]
cell.price.text = String(prices[indexPath.row])
cell.average.text = String(averages[indexPath.row])
//but is there something I need to add here to keep the correct Stepper and Label for each class?
return cell
}
One of the already asked questions includes a protocol and another function in the ViewController like this
protocol ReviewCellDelegate{
func stepperButton(sender: ReviewTableViewCell)
}
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
}
}
I don't know if this is the approach I should be trying to take. I am looking for the simplest solution, but I am having trouble putting the pieces together.
Any help is appreciated.
Thanks.
Easiest solution (simplyfied):
Create a model BuyStat with a property purchaseAmount (it's crucial to be a class).
You are strongly discouraged from using multiple arrays as data source
class BuyStat {
var purchaseAmount = 0.0
init(purchaseAmount : Double) {
self.purchaseAmount = purchaseAmount
}
}
In the view controller create a data source array
var stats = [BuyStat]()
In viewDidLoad create a few instances and reload the table view
stats = [BuyStat(purchaseAmount: 12.0), BuyStat(purchaseAmount: 20.0)]
tableView.reloadData()
In the custom cell create a property buyStat to hold the current data source item with an observer to update stepper and label when buyStat is set
class BuyStatsCell: UITableViewCell {
#IBOutlet weak var purchaseAmount: UILabel!
#IBOutlet weak var addSubtract: UIStepper!
var buyStat : BuyStat! {
didSet {
addSubtract.value = buyStat.purchaseAmount
purchaseAmount.text = String(buyStat.purchaseAmount)
}
}
#IBAction func stepperAction(_ sender: UIStepper) {
buyStat.purchaseAmount = sender.value
self.purchaseAmount.text = String(sender.value)
}
}
In cellForRowAtIndexPath get the data source item and pass it to the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BuyStatsTabCell", for: indexPath) as! BuyStatsCell
cell.buyStat = stats[indexPath.row]
return cell
}
The magic is: When you are tapping the stepper the label as well as the data source array will be updated. So even after scrolling the cell will get always the actual data.
With this way you don't need protocols or callback closures. It's only important that the model is a class to have reference type semantics.
NOTE: MY Cell class is just normal..All changes are in viewcontroller class
class cell: UITableViewCell {
#IBOutlet weak var ibAddButton: UIButton!
#IBOutlet weak var ibStepper: UIStepper!
#IBOutlet weak var ibCount: UILabel!
#IBOutlet weak var ibLbl: UILabel!
}
1.define empty int array [Int]()
var countArray = [Int]()
2.append countArray with all zeros with the number of data u want to populate in tableview
for arr in self.responseArray{
self.countArray.append(0)
}
3.in cell for row at
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! cell
let dict = responseArray[indexPath.row] as? NSDictionary ?? NSDictionary()
cell.ibLbl.text = dict["name"] as? String ?? String()
if countArray[indexPath.row] == 0{
cell.ibAddButton.tag = indexPath.row
cell.ibStepper.isHidden = true
cell.ibAddButton.isHidden = false
cell.ibCount.isHidden = true
cell.ibAddButton.addTarget(self, action: #selector(addPressed(sender:)), for: .touchUpInside)
}else{
cell.ibAddButton.isHidden = true
cell.ibStepper.isHidden = false
cell.ibStepper.tag = indexPath.row
cell.ibCount.isHidden = false
cell.ibCount.text = "\(countArray[indexPath.row])"
cell.ibStepper.addTarget(self, action: #selector(stepperValueChanged(sender:)), for: .valueChanged)}
return cell
}
4.objc functions
#objc func stepperValueChanged(sender : UIStepper){
if sender.stepValue != 0{
countArray[sender.tag] = Int(sender.value)
}
ibTableView.reloadData()
}
#objc func addPressed(sender : UIButton){
countArray[sender.tag] = 1//countArray[sender.tag] + 1
ibTableView.reloadData()
}
How to solve the table view data overlapped issue?
I have searched some Object C version answer, but it seems not working for Swift. I tried to get cell==nil, but it gives me an error
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let current_patient = patients[indexPath.row]
let cell = myTable.cellForRowAtIndexPath(indexPath) as! PatientTableViewCell
if (cell){
let cell = myTable.dequeueReusableCellWithIdentifier("patientCell") as! PatientTableViewCell
}
let cell = myTable.dequeueReusableCellWithIdentifier("patientCell", forIndexPath: indexPath) as! PatientTableViewCell
if(cell != nil){
var subviews = cell.contentView.subviews
subviews.removeAll()
}
//configure cell
let type = current_patient.enrollable_type
cell.patientTypelbl.text = type
return cell
}
After two-days struggling, I figure it out finally. The key to solve it is to find which cell is been resued. What I am doing now is give a var identifier to cell.
class PatientTableViewCell: UITableViewCell {
#IBOutlet weak var followupBtn: UIButton!
#IBOutlet weak var viewBtn: UIButton!
#IBOutlet weak var titleType: UILabel!
#IBOutlet weak var titleID: UILabel!
#IBOutlet weak var patientTypelbl: UILabel!
#IBOutlet weak var patientIDlbl: UILabel!
**var identifier = false**
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let current_patient = patients[indexPath.row]
var cell = myTable.dequeueReusableCellWithIdentifier("patientCell") as! PatientTableViewCell
if(cell.identifier == true){
cell = myTable.dequeueReusableCellWithIdentifier("patientCell") as! PatientTableViewCell
}
//config cell
cell.identifier = true //important
I hope it can help someone else. :)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let current_patient = patients[indexPath.row]
let cell: PatientTableViewCell! = tableView.dequeueReusableCellWithIdentifier("patientCell") as? PatientTableViewCell
//configure cell
let type = current_patient.enrollable_type
cell.patientTypelbl.text = type
return cell
}