How to modify a custom cell inside of DidSelectRowAtIndexPath - swift

A custom cell class has the override func layoutSubviews() where the detailTextLabel of each cell is given the title "Jim". Upon clicking on DidSelectRowAtIndexPath, is there a way to change the detail text of the cell permanently(to stop the cell from constantly making the detail Jim), to let's say "Bob"?.
//This function is in the custom cell UserCell
class CustomCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
detailTextLabel?.text = "Jim"
}
///........
}
//In the viewController with the tableView outlet
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//.....
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! CustomCell
//......
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
/* Code I need to convert the detailTextLabel.text to equal "Bob" upon clicking on certain cell */
}

The cell itself shouldn't be used to hold state for any data, but only to display it. Create a mutable array property on the controller to hold the underlaying data (strings). Set the new cells' text properties by reading this array, and in tableView:didSelectRowAtIndexPath: change the value in the array at the index for "Bob" to "Jim". Whenever the tableView reloads it will now read the updated value from the dataSource.
In addition to the UITableViewDelegate protocol also study the UITableViewDataSource protocol. By default the UITableViewController class conforms to both of these protocols and is assigned as each on its .tableView property (if you introspect its self.tableView.delegate and self.tableView.datasource values you will receive back the original UITableViewController). If you manually created your own tableview controller class that inherits from UIViewController, then you will need to assign both of these properties on the tableView in order for it to function properly.

Very simple, do it like this:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
detailTextLabel?.text = "Bob"
}

Related

Connect Prototype Cells to View Controller [Swift 4]

I am new to programming and currently working on a newsfeed like app. I had a normal Table view up and running fine, but want to try it now with a costume cell type. So I created one and thought connecting the labels the usual way would be perfectly fine, but I was wrong. So I am wondering how I can let my Text label connect to my view controller, so I can use my custom cell.
class ViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var newsfeedTableView: UITableView!
var ref: DatabaseReference!
var posts = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (posts.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")
//here is where I need the custom label to get the posts
cell.textLabel?.text = posts[indexPath.row]
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 18.0)
return cell
}
}
Create subclass of UITableViewCell and connect IBOutlets to this class
class YourCell: UITableViewCell {
#IBOutlet weak var customLabel: UILabel!
...
}
don't forget to set class of your prototype cell in storyboard:
then in cellForRowAt data source method downcast your dequeued cell as YourCell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCell
then you have access to YourCell outlets
cell.customLabel.text = "SomeText"
...
I'm assuming that you are using Storyboard.
First of all, you should understand that there is little difference when you use own custom table cell. In that case, in the method "cellForRowAtIndexPath", after dequeue your cell, you just have to typecast table cell like 'as! YourCustomTableCellClass'. After this line, you can access each property of this class.
First, design your table cell on Storyboard whatever you want.
Now, make a subclass of UITableViewCell and assign this class to your prototype custom cell which you have designed on Storyboard. Also, don't forget to set "reuse identifier in Storyboard table cell.
Then connect your outlets with custom cell class from Storyboard.
Now you can use code like this:
class YourTableCellClass: UITableViewCell {
// I'm using these outlets as a sample, but you have to connect them from Storyboard.
var leftTextLabel: UILabel!
var rightTextLabel: UILabel!
}
class YourTableController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - TableView Delegate & DataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // return your number of rows here...
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // return the height for the row here.....or you can set height from Storyboard if you have fix height of all rows.
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! YourTableCellClass
cell.leftTextLabel.text = "Text" // Set text here....
cell.rightTextLabel.text = "Text" // Set text here....
return cell
}
}

Push TableViewController from a UICollectionView with UITableViewCell's

So I am trying to push a UITableViewController from a UITableViewCell that is embedded in a collection view cell.
So the structure is UICollectionView > UICollectionViewCell > UITableView > UITableViewCell.
Do I want to call a segue when the TableViewCell is clicked?
How can I do this as you cannot access the function perform segue for identifier?
You can create one protocol for that and implement it with your ViewController where you are having collectionView, after that make instance property of that protocol in your collectionViewCell and set that property in cellForItemAt method. Now inside the didSelectRowAt method of tableView call the delegate method with the detail that you want to pass for segue.
protocol PassData {
func performSegue(with data: String) //Set argument type to Type that you want pass instead of String
}
Now implement this PassData protocol with your ViewController and inside the cellForItemAt set the delegate of UICollectionViewCell.
class ViewController: UIViewController, PassData, UICollectionViewDelegate, UICollectionViewDataSource {
//Other methods
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
cell.passDelegate = self
return cell
}
//Add the `performSegue(with:)` delegate method of PassData
func performSegue(with data: String) {
//Perform segue here
self.performSegue(withIdentifier: "SegueIdentifier", sender: data)
}
}
Now in your CustomCollectionViewCell make one instance property named passDelegate of type PassData? after that in didSelectRowAt method of tableView call its delegate method.
class CustomCollectionViewCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {
var passDelegate: PassData?
//Your other methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Pass whatever data that you want to pass
self.passDelegate?.performSegue(with: array[indexPath.row])
}
}
You can push the tableViewController to navigation stack by calling self.navigationController?.pushViewController(tableviewVC, animate: true) from table view delegate method didSelectRowAtIndexPath

How to change an UIImageView into a custom cell

I have an UIImageView in a custom cell (#IBOutlet var imageSquadra: UIImageView!) and in the TableViewController I initialize this imageView:
let squadra = DataManager.sharedInstance.arrayCori[indexPath.row]
cell.imageSquadra.image = squadra.sfondo1
Now I need to change the image touching up inside the cell with an other image, and return to the previous image touching up into an other cell.
Is it hard to do?
EDIT: or if was easier, would be fine apply a CAFilter like a shadow to the image.
On your tableview set multiple selection to NO:
tableView.allowsMultipleSelection = false
Then you want something like this on your TableViewController
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Store which indexPath has been selected in a global variable
tableView.reloadData()
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
tableView.reloadData()
}
In your cellForRow method you want something like...
cell.useSelectedImage(indexPath == storedIndexPath)
And finally in your custom cell class you want to add a method that does similar to this
func useSelectedImage(bool:useSelected)
{
self.myImage = useSelected ? "selectedImage" : "unselectedImage"
}
My Swift is a bit rusty... but you should be able to get the general idea from what I've done here

UITableView, does not conform to protocol - how to declare several?

I understand that I need to implement required methods in the controller and establish a relationship between view and controller.. but where do the identifier "tableView" in
func tableView(mintabell: UITableView, numberOfRowsInSection section: Int) -> Int
{
return items.count
}
come from and what if I want several tableviews on same view? How to declare them separately?
These are the protocols that you need to declare for a TableView
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return createCellAndReturnItHere
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCellsInSection
}
In terms of having more than one tableview in one ViewController.. If you have an outlet for each tableview then you can check which one you need inside the protocol functions:
#IBOutlet weak var tableViewOne: UITableView!
#IBOutlet weak var tableViewTwo: UITableView!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.tableViewOne {
return createCellForTableViewOneAndReturnItHere
} else {
return createCellForTableViewOneAndReturnItHere
}
}
Think of it like a box. Every tableView that is subscribed to the protocol grabs in that box leaves its fingerprint and takes what it gets. So if you have multiple tableViews in one controller you can distinguish them by checking for equality.
Example:
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let firstTableView = UITableView()
let secondTableView = UITableView()
viewDidLoad() {
firstTableView.delegate = self
secondTableView.delegate = self
firstTableView.dataSource = self
secondTableView.dataSource = self
}
// ... some othe methods...
func tableView(mintabell: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView == firstTableView {
return 10
}
if tableView == secondTableView {
return 20
}
return 0
}
}
Delegates are implemented by the developer and called by iOS not the developer. The tableView is passed by iOS and points to the particular tableview.
If you have several tableviews with the same delegate you can compare the passed tableview parameter to the tableviews you have implemented to determine which one.
Alternatively, create a tableView delegate and datasource per tableView. This will eliminate testing which tableView removing a lot of conditional logic from the code.
First of all, you don't necessarily always have to implement a protocol for every view. Only when you have a view which has delegate that needs implementing, you will have to conform that protocol responsible for that delegate implementation.
So for tableView, first you drag a UITableView in your Controller from the Object Library and then a UITableViewCell under the Table View.
Now go to the ViewController.swift file and add
#IBOutlet var myFirstTableView: UITableView!
right after the line.
class ViewController: UIViewController
P.S: If you have more than one table then you could just declare the extra table's here., like-
#IBOutlet var mySecondTableView: UITableView!
Now, let's assume, you have one table. Now, you need to add the list of protocols. So, just add UITableViewDelegate, UITableViewDataSource with
class ViewController: UIViewController
appended by comma.
Adding this should give you an error but that's okay. This is because you have not added the required method listed under that UITableViewDatasource protocol.
So, just add those required methods and implement it accordingly.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("myCell") as! UITableViewCell
cell.textLabel?.text = "test"
return cell
}
Here, I said, I will have 3 cells whose identifier is "myCell" and the cell's textLabel will have a text, "test".
Now, we forgot a very important step and that is to assign the cell identifier to our TableView Cell. So, go to the storyboard and select the TableView Cell and insert "myCell" as an identifier for your cell.
If you had more than one table, then you would check for which TableView, you are loading your data. So, you could assign a unique tag to each table explicitly(you can do that either from storyboard or from code), and based on that tag, you would implement your methods. Let's say you have 3 tables and the assigned tag is 1,2 and 3. So, you could do something like,
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1{
return 3
}
else if tableView.tag == 2{
return 4
}
else{
return 1
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
if tableView.tag == 1{
cell.textLabel?.text = "test1"
}
else if tableView.tag == 2{
cell.textLabel?.text = "test2"
}
else{
cell.textLabel?.text = "test"
}
return cell
}

UITableViewDataSource doesn't get invoked

I am trying to learn swift which is also my first programming language. I am trying learn by creating an app with table view in it. I have added a table view and table cell(myCell as identifier). Table cell in turn has labels and text field. Also the background color of the table cell is yellow.
The view controller which encapsulates all this is hooked to class showDetailViewController as show below.
class showDetailViewController:UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: TaskCell = tableView.dequeueReusableCellWithIdentifier("myCell") as TaskCell
return cell
}
//UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
When I run my application I expect to see my cell replicated 5 times (as number of cells in section returns 5) in table view but upon debug I realized that the no tableview delegate class get invoked and hence the issue.
Can anyone please help me with this issue?
Thanks,
Dev
Add the tableView as an IBOutlet.Then you should set tableView's datasource to viewController. self.tableView.dataSource = self. You can also set tableView's dataSource in Interface bulider.