How to change certain cell's view across different view controllers swift - swift

I am building a recorder app that users can save their recordings.
How can I change a view of a certain cell in a UITableView to show users that this cell is being played and the cell with the same content in other ViewControllers can be changed simultaneously?
Furthermore, when the audio player finished. The view can be changed back to default.
I have tried override setSelected, but the cell cannot stay selected when I reload the table view and the cell cannot be deselected by itself when audio has finished. I also cannot change the cell with the same content (same audio)' view simultaneously.
Is there a way to overcome this problem?
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
if selected {
print("task has began")
} else if !selected {
print("task has finished")
}
}
The result will be like apple music or podcast that the cell with the same shows across the app will show whether or not it is being played.

First make IBOutlet of View Inside xib
class TableViewCell: UITableViewCell {
#IBOutlet weak var generaV: UIView!
}
set Identifier = TableViewCell
and call the Datasource Method in as many file you want and change the color like .red/.blue in in different VC
Call the Cell in ViewController
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
if (audioEnded){
cell.generaV.backgroundColor = UIColor.green
}
else {
cell.generaV.backgroundColor = UIColor.red
}
return cell
}

Related

awakeFromNimb doesn't get called on TableView reloadData Swift

I have an iPad TabBar navigation app and in OrderViewController I have a tableview on left side and outlets on the right side of the view as in a sort of split view controller. When you click a tableView's cell it shows details on the other half screen. As I'm introducing two different color schemes for A/B testing, I use a switch to perform the change in color. The color switching dough doesn't succeed with tableView's cells. Adding a reloadData() in viewWillAppeardidn't solve the problem.
Everything gets updated except for the cells, they maintain the colors of when they got created the first time.
I added prints throughout the phases and I detected that cell's awakeFromNimbgets called only the first time. How do I get it to be called on tableView.reloadData()? As always many thanks.
awakeFromNib():
override func awakeFromNib() {
super.awakeFromNib()
configureUi()
}
viewWillAppear():
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.configureFetchedResultsController()
orderTableView.delegate = self
orderTableView.dataSource = self
configureUI()
orderTableView.reloadData()
}
Use the tableView delegate willDisplayCell
https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614883-tableview
class MyCustomVC: UITableViewDelegate {
override func tableView(_ tableView: UITableView,
willDisplay cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
guard var cell = tableView.dequeueReusableCell(withIdentifier: "yourReuseIdentifier", for: indexPath) as? YourCustomCell else {
return UITableViewCell()
}
cell.configureUi()
}
}

How to keep an object's reference when using UITableView?

The problem I'm facing is probably some lack of understanding on the concept of reusable cells. I have, let's say, 30 rows to be created and each of them has a UISwitch.
When I toggle one of the switches, it's behavior should affect the other 29. The point is: as far as I know, iOS doesn't create all of them at once, but rather wait to reuse the cells when the TableView is scrolled up and down.
How can I keep a copy of those reused objects and tell iOS to set the proper value to the switches?
I've thought on having the cells appended to a [UISwitch] but I can't manage to have all the 30 cells in there, look:
...
var switches = [UISwitch]()
...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Field10Cell", for: indexPath) as! Field10TableViewCell
...
//cell.value is a UISwitch
if !switches.contains(cell.value) {
switches.append(cell.value)
}
return cell
}
You could create a set which stores the indexes of the cells whose switches have been pressed.
var activeSwitches = Set<IndexPath>()
Whenever a user presses the switch on a cell, you store it on the set like this:
activeSwitches.insert(indexPath)
If you need to check if a switch was activated, just check if its container cell's indexPath is in active switches like so:
if activeSwitches.contains(indexPath) {
// do something
}
In order to know when a user pressed a specific switch I recommend the folliwing:
In cellForRowAtIndexPath save the current indexPath into your Field10TableViewCell.
Create a protocol on Field10TableViewCell and add a delegate.
protocol Field10Delegate {
func didChangeSwitch(value: Bool, indexPath: IndexPath)
}
class Field10TableViewCell {
var delegate: Field10Delegate?
var indexPath: IndexPath?
#IBOutlet weak var fieldSwitch: UISwitch! // Can't use 'switch' as a variable name
#IBAction func switchValueChanged(_ sender: UISwitch) {
if let indexPath = indexPath {
delegate?.didChangeSwitch(value: sender.isOn, indexPath: indexPath)
}
}
When you create a cell, set the view controller as a delegate
let cell = tableView.dequeueReusableCell(withIdentifier: "Field10Cell", for: indexPath) as! Field10TableViewCell
cell.delegate = self
cell.indexPath = indexPath
Make your view controller comply with the protocol:
extension ViewController: Field10Delegate {
/* Whenever a switch is pressed on any cell, this delegate will
be called. This is a good place also to trigger a update to
your UI if it has to respond to switch changes.
*/
func didChangeSwitch(value: Bool, indexPath: IndexPath) {
if value {
activeSwitches.insert(indexPath)
} else {
activeSwitches.remove(indexPath)
}
updateUI()
}
}
With the above, at any point you will know which switches are active or not and you can process the dequeued cells with this information.

How to change cell editing style with animation

In the Clock app on iOS 10, toggling the cells between editing and the default style comes with a smooth animation transition (as seen in the recording below)
I am trying to replicate this transition for a simple app containing a tableview but I do not know how to implement the animation transition.
My app code for the editing style change is below
ViewController.swift
...
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//Model array for cell data - omitted population of array for simplicity
var notes = [Note]()
//cell editing style
var cellStyleForEditing: UITableViewCellEditingStyle = .none
//The tableview
#IBOutlet weak var NoteTable: UITableView!
...
#IBAction func enableEdittingBttn(_ sender: AnyObject) {
if(cellStyleForEditing == .none) {
cellStyleForEditing = .delete
self.NoteTable.reloadData()
} else {
cellStyleForEditing = .none
self.NoteTable.reloadData()
}
}
//delegate function sets cell editing style on table load/reload
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return cellStyleForEditing
}
...
//omitted tableview delegate methods
}
As you can see, I achieve the cell style change by reloading the table data after changing the table's cell editing style.
I have omitted all irrelevant code for simplicity.
Instead of reloading your table's data when enabling or disabling editing, call setEditing(_:animated:) with animated true.
if(cellStyleForEditing == .none) {
cellStyleForEditing = .delete
} else {
cellStyleForEditing = .none
}
NoteTable.setEditing(cellStyleForEditing != .none, animated: true)

pass data from tableview to view controller

I am writing a Xcode program in Swift. I have a tableview controller with some labels and an image per cell. They have their data from a first view controller. So far so good. Now i would like the user to tap a cell which opens a new controller which contains the same label and image data as the cell. With the following code the new controller opens, nonetheless I don't no how to transfer the data. If someone could help me I would be so so grateful.
Here are the codes that i tend to use:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "TableView"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableView
let picture = pic[indexPath.row]
cell.label1.text = picture.name1
cell.photoImage.image = picture.photo
cell.label2.text = picture.name2
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let cell = indexPath.row
self.performSegueWithIdentifier("segue", sender: cell)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue"{
var row = tableView.indexPathForSelectedRow
let viewController = segue.destinationViewController as! vc2
}
}
PS:I have a segue from the image to the new controller with the identifier "segue".
PPS: Of course i have tried the following method: Send data from TableView to DetailView Swift but when i run my program I get an error with the information that my labels were unexpectedly found nil
You should not need to override didSelectRowAtIndexPath or call performSegueWithIdentifier to do this. Connect your segue in the IB file dragging from a table view controller's cell to the second controller. You should then pass the data to the controller in prepareForSegue, in the segue.destinationViewController. You set the public properties on that destination controller.
Also make sure your labels in your prototype cell have been connected in IB. If they are they, should not be nil. Set a breakpoint on the cellForRowAtIndexPath to verify this.
Unless you are talking about the labels in your destination controller. These also need to be hooked up in IB. You would then set them in prepareForSegue. You would get the appropriate data from the
let viewController = segue.destinationViewController as! vc2
var path = tableView.indexPathForSelectedRow
viewController.destLabel.text = arrayData[path.row].myData // or whatever data you have from your model.

Issue with Swift TableViewCell: change background color for selected row

I have a strange issue with my tableView.
I have a List of audio tracks and a segue to an audio player in order to play the selected track at a specific row. Everything works fine!
I wanted to change the background color for the selected row in the table so that, once the user play the audio and come back to the list of tracks (my Table View Controller) , he can see which are the previously selected rows.
But when I run It change me the color not only for the row at index path I selected but also to the item at index path + 10.
If I select the First Row it change me the color for the row at the index: 0, 10, 20, 30...
In order to change the color of the selected cell I did the follow:
// MARK: - Navigation
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("audioPlayer", sender: tableView)
var selectedCell:UITableViewCell = tableView.cellForRowAtIndexPath(indexPath) as CustomTableViewCell
selectedCell.contentView.backgroundColor = UIColor.greenColor()
selectedCell.backgroundColor = UIColor.blueColor()
Please find a screenshot of my issue, I have selected just three rows: 1, 3, 5 but I get selected 1,3,5,11,13,15,21,23 and so on... :
https://www.dropbox.com/s/bhymu6q05l7tex7/problemaCelleColore.PNG?dl=0
For further details - if can help - here it is my Custom Table View class:
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var artista: UILabel!
#IBOutlet weak var brano: UILabel!
var ascoltato = false
#IBOutlet weak var labelRiproduciAscoltato: 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
}
func setCell(artista: String, brano: String){
self.artista.text = artista
self.brano.text = brano
}
} // END MY CUSTOM TABLE VIEW CELL
Here it is the method cellForRowAtIndexPath in my TableViewController:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as CustomTableViewCell
var tracks : Brani //Brani is my custom Object for handle my tracks
cell.setCell(tracks.title!, brano: tracks.author!)
return cell
}
I am running on iPad Air with iOS 7.1.
Thank you in advance for any suggestion or advice related to my issue.
This is probably because UITableViewCells are recycled. This means the formerly selected tableViewCell gets reused by the cells at the lower indexes. This is expected behavior of a UITableView and makes sense, as it saves memory usage. To fix the issue, you will need to have your datasource keep a track of which cell is selected, and updated the cell's background color accordingly.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("audioPlayer", sender: tableView)
//datasource is updated with selected state
//cell is updated with color change
}
Then in your cellForRowAtIndexPath method:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as CustomTableViewCell
var tracks : Brani //Brani is my custom Object for handle my tracks
cell.setCell(tracks.title!, brano: tracks.author!)
//update cell style here as well (by checking the datasource for selected or not).
return cell
}