How to detect that a different collection view cell has been selected? - uicollectionview

I am developing a music app whereby selecting a collection view cell plays a track - I wish for the cell, when selected/tapped, to play when first selected and to pause if selected/tapped again. I can effectively play and pause when the same cell is selected however the problem arises when I select a different cell. How do I seperate the logic so that I can find out that a new cell has been selected? (and can therefore play and pause another track). I've tried didSelectItemAt delegate method but that gets called every time the cell is selected and I cannot figure out how to detect whether a different cell has been selected or not.
The behaviour I am looking for in other words: cell 1 is tapped - track 1 plays, cell 1 is tapped again - track 1 pauses OR cell 1 is tapped - track 1 plays, cell 2 is tapped - track 2 plays.
Any help would be greatly appreciated.
P.S. I'm using Swift
Visual representation of the App (a collection view where each cell is a seperate track)
EDIT
var currentTrack: Int!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
currentTrack = indexPath.item
switch selected {
case true:
playAudio()
case false:
//Trying to match the current indexPath against the selected cell so I can play and pause that one
if currentTrack != indexPath.item {
playAudio()
} else {
pause()
}
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
pointerArray[keys[indexPath.item]] = false
print("Stop", keys[indexPath.item])
}

You can user the indexPath to check which item is being selected. Im gonna use print as an example but you can add the code for playing the same way.
var songArray = ["SongOne", "SongTwo", "SongThree"]
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(songArray[indexPath.item])
}
EDIT
In that case I would set a dictionary for each song and a pointer array to check whether the current track is playing or not. Also. if you want to stop the current track as the new one starts playing you can use the didDeselectItemAt function. Here is the code using both functions and print just as an example:
var songKeys = ["SongOne", "SongTwo", "SongThree"]
var songArray = ["SongOne" : false, "SongTwo" : false, "SongThree" : false]
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if songArray[songKeys[indexPath.item]] == false {
songArray[songKeys[indexPath.item]] = true
print("Playing", songKeys[indexPath.item])
return
}
songArray[songKeys[indexPath.item]] = false
print("Stop", songKeys[indexPath.item])
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
songArray[songKeys[indexPath.item]] = false
print("Stop", songKeys[indexPath.item])
}

Related

index not being transferred from uicollectionviewcell to view controller

My code below is not working and is causing a runtime error. Stating Unexpectedly found nil while unwrapping an Optional value. I don't know what is causing this. I don't know how to fix the optional issue in this. The code for cellforitemat has also been added.
class antion : UICollectionViewCell {
#IBOutlet var sam : UILabel!
var deleagete : DataCollectionProtocoe?
var index : IndexPath?
#IBAction func press(){
deleagete?.passData(indx: (index?.row)!)
}
#IBAction func delete(){
deleagete?.deleteData(indx: (index?.row)!)
}
}
extension ViewController : DataCollectionProtocoe {
func passData(indx: Int) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailVDC") as? DetailVDC
vc?.name = people[indx].name!
self.navigationController?.pushViewController(vc!, animated: true)
}
func deleteData(indx: Int) {
people.remove(at: indx)
cc.reloadData()
}
}
protocol DataCollectionProtocoe {
func passData(indx:Int)
func deleteData(indx:Int)
}
class DetailVDC: UIViewController {
var name = ""
#IBOutlet var lbl : UILabel!
override func viewDidLoad() {
lbl.text = name
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! antion
cell.index = indexPath
cell.deleagete = self
return cell
}
Based on what little i could understand from your code, it looks like you are creating the UICollectionViewCell instance but not setting any value to the delegate property of the cell.
When you create an instance of the collection view cell in the controller , assign the controller to the delegate property of the cell as follows:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = antion()
cell.delegate = self // assuming the controller is the data source of the collection view
}

Create UICollectiionViewCell based on UIView

I am trying to have a UICollectionView that holds different UIViews as its cells. Is this possible or do I have to make them UICollectionViewCells?
I don't have an objective-c example, but you should be able to get the concept from the code example below.
An example how you can create a cell that wraps a UIView and is more reusable
class ProfileView: UIView {
var imageView: UIImageView!
var name: UILabel!
}
class ProfileCollectionViewCell: UICollectionViewCell {
let profileView = ProfileView()
init() {
super.init()
configureConstraints()
}
func configureConstraints() {
// use a handy extension you've already built
contentView.addSubView(profileView)
profileView.pinToEdges(of: contentView)
}
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let row = self.objects[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "someId" for: indexPath) as? ProfileCollectionViewCell
cell?.profileView.imageView.image = row["image"]
cell?.profileView.name.text = row["name"]
return cell
}
note: you may need to manage 'resetting the cells state' before it gets reused with something like:
override prepareForReuse() {
super.prepareForReuse()
profileView.imageView.image = nil
profileView.name.text = ""
}
You have to return UICollectionViewCells. UICollectionView don't accept UIViews.
What you can do is create a generic UICollectionViewCell that can embed any UIView.
The reason is because collection view cells have specific composition for layout and recycling.
Also, you add child in your UIView directly on the view itself, collection view cells have a contentView, like UITableViewCells.

Downpicker not appearing in UITableView

I am attempting to use the Darkseal Downpicker inside a UITableView. I am programatically adding a UITextField and then adding the Downpicker as follows:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var textField = UITextField()
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.selectionStyle = .none
textField.text = "TEST"
textField.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(170), height: CGFloat(30))
var dp: DownPicker!
let test1 = ["test one", "test two"]
dp = DownPicker(textField: textField, withData: test1)
cell.contentView.addSubview(dp)
return cell
}
If I just add the textfield:
cell.contentView.addSubview(textField)
Then it displays the text field. However if I add the DownPicker as shown above:
cell.contentView.addSubview(dp)
I get nothing displayed, just an empty tableview. Any help understanding why the DownPicker is not displayed would be appreciated. Thanks.
You need to add both ur UITextField and ur DownPicker into the View. If it's anything like the UIPickerView then your DownPicker will only show when you select the UITextField. Also you need to add your DownPicker to the view rather then the cell.
view.addSubView(dp)
EDIT
"It takes any UITextField already present in your code (including those added to a Storyboard):" - https://github.com/Darkseal/DownPicker
EDIT 2
From looking at the docs what you want to do is add yourself as the observer on the DataPicker by doing the following inside the cellForRowAt func
dp.addTarget(self, action: #selector(dp_selected(sender:)), for: .valueChanged)
You want to add a func the class with cellForRowAt func inside it
func dp_selected(sender: AnyObject) {
guard let dp = sender as? DropDown else { return }
if let textfield = dp.getTextField() as? UITextField {
guard let indexPath = tableView.indexPath(for: textfield.superview) else { return }
}
}
This function will check if the sender is of type DropDown class and if it is will try to get the UITextField that it's attached to and if that is returned it will attemt to get the IndexPathat which it's being used at this can be nil if the cell is no longer visible on the screen but hopefully that will never be the case and have added a check to make sure it exists.

TableView is not counting exactly

I pass data(value) from a ViewController to a TableView (see below), but the tableview fill always only the first row. It donĀ“t count. How can I fix it?
The tableview should show every passing data in a new row.
#IBAction func a(sender: UIButton) {
txtBalkenbewehrung = ausgabe.text
performSegueWithIdentifier("transferfile", sender: sender)
}
import UIKit
var txtBalkenbewehrung: String?
class EBTableViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
var rowData = [txtBalkenbewehrung]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.editing = true
self.tableView.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return rowData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("textcell",
forIndexPath: indexPath) as! UITableViewCell
// let data = rowData[indexPath.row]
cell.textLabel!.text = rowData[indexPath.row]
return cell
}
`
First, please make sure that you correctly format your code!
The behavior seems coherent, as the data is a an array with a single element, txtBalkenbewehrung
You need to fill the array with more element if you want to display more than one

Pass Data to next ViewController Using UIBarButtonItem in Swift

I have a UITableView that allows multiple selections. I'd like to pass the selections to the next "Q2ViewController" when user clicks the rightBarButtonItem "Next".
Note: I'm not using Storyboard. After searching through, I haven't found a solution without using storybaord.
var options = ["breakfast", "lunch", "dinner", "dessert"]
var selected = -1
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Q1View"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .Done, target: self, action: "didTapNext:")
self.tableView.allowsMultipleSelection = true
var nib = UINib(nibName: "OptionCell", bundle: nil)
tableView?.registerNib(nib, forCellReuseIdentifier: "OptionCellIdentifier")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.options.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("OptionCellIdentifier", forIndexPath: indexPath) as OptionCell
cell.textLabel?.text = self.options[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
Anywhere in your code you must create the "next" view controller. Either in your App Delegate or directly in the view controller shown above.
If you create the view controller outside the above one you will have to pass the above controller a reference to the destination view controller.
If you create the view controller inside the above one save a reference to it in an instance variable.
Or create a method that creates the VC on pressing the "Next" button and pushes the VC to the navigation controller.
You can use UITableView's indexPathsForSelectedRows() or similar to get the selection and then call a method/set a instance variable on the destination view controller.
But seriously, why don't you use storyboards?