简体   繁体   中英

Can I add an action to a customCell in a UICollectionView?

First I had a couple of UIButtons with a @IBAction to play a sound when tapped. Because I had some problems with constraints on different iPhones I decided to go for a UICollectionView with CustomCells. Now I got this working I want to setup the CustomCells to play a sound when tapped. So I think I will have to setup something like an 'Action' on the custom cells for playing a sound. I do have a 'sort of action' that prints out that the user tapped a custom cell like;

print("tapped tag > ", self.tag)

The original @IBAction I used on a UIButton was:

@IBAction func sound2(_ sender: UIButton) {
sound2.play()

Again, I 'think' I need to setup a sort of action to the CustomCell for playing a sound? And I 'think' I need to declare somewhere what sound to play along with the self.tag? So each customCell will play a different sound..

The only problem, (rookieAlert) I don't know where to start :( But I want to learn! Can somebody point me into the right direction or provide me with some information?

Here is the full code of the project to make things more clear:

import UIKit
import AVFoundation

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    
    var countries = [UIImage(named:"c1"),UIImage(named:"c2"),UIImage(named:"c3"),UIImage(named:"c4"),UIImage(named:"c5"),UIImage(named:"c6"),UIImage(named:"c7"),UIImage(named:"c8"),UIImage(named:"c9"),UIImage(named:"c10")]
    
    @IBOutlet weak var soundsCollectionView: UICollectionView!
    
    lazy var cv: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical
    layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    layout.itemSize = CGSize(width: 100, height: 100)
        
    var cc = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    cc.translatesAutoresizingMaskIntoConstraints = false
    cc.register(CustomCell.self, forCellWithReuseIdentifier: "CustomCell")
    cc.delegate = self
    cc.dataSource = self
    cc.backgroundColor = .black
    return cc
    }()
    
    let sounds = ["sound1", "sound2", "sound3", "sound4", "sound5"]
    
    var sound1 = AVAudioPlayer()
    var sound2 = AVAudioPlayer()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            
            view.addSubview(cv)
            cv.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            cv.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
            cv.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
            cv.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            
            // Setting up sound1
            do {
                sound1 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "sound1", ofType: "mp3")!))
                sound1.prepareToPlay()
            } catch  {
                print(error)
            }
            
            // Setting up sound2
            do {
                sound2 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "sound2", ofType: "mp3")!))
                sound2.prepareToPlay()
            } catch  {
                print(error)
            }
            
        }
    
    @IBAction func sound2(_ sender: UIButton) {
        if sound2.isPlaying {
            sound2.stop()
            sound1.stop()
            sender.setImage(UIImage(named: "sound1_off.jpg"), for: .normal)
        } else {
            sound2.play()
            sender.setImage(UIImage(named: "sound1_on.jpg"), for: .normal)
        }
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.centerImageView.image = countries[indexPath.row]
        cell.tag = indexPath.row
        cell.backgroundColor = UIColor.green
        return cell
    }
    
    //define constant value as per your requirement and declare this variables globally
    let inset: CGFloat = 10 //define as per your requirement
    let minimumInteritemSpacing: CGFloat = 10 //define as per your requirement
    let cellsPerRow = 3

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            
       let marginsAndInsets = inset * 2 + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
            
       let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
       
       return .init(width: itemWidth, height: itemWidth)
            
    }
    
    
    
    
    
}

class CustomCell: UICollectionViewCell {
    
    lazy var centerImageView: UIImageView = {
        var img = UIImageView()
        img.translatesAutoresizingMaskIntoConstraints = false
        img.clipsToBounds = true
        img.isUserInteractionEnabled = true
        img.contentMode = .scaleAspectFill
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handler(_:)))
        tapGesture.numberOfTapsRequired = 1
        img.addGestureRecognizer(tapGesture)
        return img
    }()
    
    @objc private func handler(_ sender: UITapGestureRecognizer) {
        
        print("tapped tag > ", self.tag)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(centerImageView)
        centerImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        centerImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        centerImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        centerImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
        

    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

This is for same sound in all cell: try to delete gesture from your centerImage in custom cell, after that add the gesture in cellForItemAt and call the function like this:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.centerImageView.image = countries[indexPath.row]
cell.backgroundColor = UIColor.green
    
let gesture = UITapGestureRecognizer(target: self, action: #selector(playCellSound))
cell.centerImageView.addGestureRecognizer(gesture)
    
 return cell
}

after that write the func below cellForItemAt:

@objc func playCellSound() {
    sound2.play()
    print("play sound...")
}

FOR DIFFERENT SOUND IN CELLS:

the number of sounds must be equal the number of countries:

let sounds = ["sound1", "sound2", "sound3", "sound4", "sound5", "sound6", "sound7", "sound8", "sound9", "sound10"]

after that declare your sounds like this:

var sound1 = AVAudioPlayer()
var sound2 = AVAudioPlayer()
var sound3 = AVAudioPlayer()
var sound4 = AVAudioPlayer()
var sound5 = AVAudioPlayer()
var sound6 = AVAudioPlayer()
var sound7 = AVAudioPlayer()
var sound8 = AVAudioPlayer()
var sound9 = AVAudioPlayer()
var sound10 = AVAudioPlayer()

set the other sound like sound 1 and sound 2

now this is your cellForItemAt:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
 cell.centerImageView.image = countries[indexPath.row]
 cell.backgroundColor = UIColor.green
    
 return cell
}

set numberOfItemsInSection like this:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return countries.count // dynamic number of items based on your countries array
}

implement the method didiSelectItemAt

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let s = sounds[indexPath.item]
    switch s {
    case "sound1":
        sound1.play()
        print("s1")
    case "sound2":
        sound2.play()
        print("s2")
    case "sound3":
        sound3.play()
        print("s3")
    case "sound4":
        sound4.play()
        print("s4")
    case "sound5":
        sound5.play()
        print("s5")
    case "sound6":
        sound6.play()
        print("s6")
    case "sound7":
        sound7.play()
        print("s7")
    case "sound8":
        sound8.play()
        print("s8")
    case "sound9":
        sound9.play()
        print("s9")
    case "sound10":
        sound10.play()
        print("s10")
    default:
        break
    }
}

in your custom cell delete tap gesture for centerImageView:

class CustomCell: UICollectionViewCell {

lazy var centerImageView: UIImageView = {
    var img = UIImageView()
    img.contentMode = .scaleAspectFill
    img.clipsToBounds = true
    img.isUserInteractionEnabled = true
    
    img.translatesAutoresizingMaskIntoConstraints = false
    return img
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    
    contentView.addSubview(centerImageView)
    centerImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    centerImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
    centerImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    centerImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
 }
}

FOR SYSTEM SOUNDS:

replace your var declaration like this:

var sound1: SystemSoundID = 1016
var sound2: SystemSoundID = 1322
var sound3: SystemSoundID = 1110
var sound4: SystemSoundID = 1070
var sound5: SystemSoundID = 1109
var sound6: SystemSoundID = 1117
var sound7: SystemSoundID = 1106
var sound8: SystemSoundID = 1118
var sound9: SystemSoundID = 1256
var sound10: SystemSoundID = 1116

deleting setting up sound 1, 2 etc. and replace the switch case in didSelectItemAt with this:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let s = sounds[indexPath.item]
    switch s {
    case "sound1":
        AudioServicesPlaySystemSound (sound1)
        print("s1")
    case "sound2":
        AudioServicesPlaySystemSound(sound2)
        print("s2")
    case "sound3":
        AudioServicesPlaySystemSound(sound3)
        print("s3")
    case "sound4":
        AudioServicesPlaySystemSound(sound4)
        print("s4")
    case "sound5":
        AudioServicesPlaySystemSound(sound5)
        print("s5")
    case "sound6":
        AudioServicesPlaySystemSound(sound6)
        print("s6")
    case "sound7":
        AudioServicesPlaySystemSound(sound7)
        print("s7")
    case "sound8":
        AudioServicesPlaySystemSound(sound8)
        print("s8")
    case "sound9":
        AudioServicesPlaySystemSound(sound9)
        print("s9")
    case "sound10":
        AudioServicesPlaySystemSound(sound10)
        print("s10")
    default:
        break
    }
  }
}

this is not the best way but it work fine, it will make you understand how it works and help you to learn

I believe what you are looking for is implementation of the didSelectItemAt function. You can implement like so by putting with your other collection view functions like cellForItemAt . Then, make sure the required sound is closely linked with your data source. Meaning you can access the sound you want via indexPath.item , which is a way of identifying the specific cell that got tapped.

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // Use indexPath.item to determine what sound to play
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM