我将尽力解释这一点。 我正在创建一个声音板应用程序。所有程序都没有StoryBoard。该应用程序使用UICollectionView在单元格中容纳所有引号名称。引号也是按钮。每个单元格都是一个按钮,应与特定的声音wav文件相对应。
我正在使用星球大战的声音作为对应用程序的测试-如果发布大多数代码,可能会更容易。
我想将测试声音"blaster-firing"
分配给单元名称"Test Name"
,将声音"yodalaughing"
分配给单元名称"Test Name 2"
我的代码在工作,单元格正在填充;当您按一下单元格的名称时,它只播放爆破声音,这是因为我只为button1设置了1个按钮标签,所以每个单元格都在执行button1。
是否可以创建与我创建的报价相关的其他按钮标签?我不确定执行此操作的最佳方法是什么。
这是我的ViewController:
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
//add different quotes for cells
let quotes = [Quote(name:"Test Name"),
Quote(name: "Test Name2")]
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.darkGray
navigationItem.title = "Board"
navigationController?.navigationBar.barTintColor = UIColor(red: 0/255, green: 200/255, blue: 255/255, alpha: 1)
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white, NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 20)]
collectionView?.register(SoundBoardCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return quotes.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! SoundBoardCell
cell.quote = quotes[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width / 1) - 16, height: 100)
}
private func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insertForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
}
我还有另一个名为SoundBoardCells
class SoundBoardCell: UICollectionViewCell, AVAudioPlayerDelegate {
//let testSoundFiles = ["baster-fire", "yodalaughing"]
var audio1 = AVAudioPlayer()
var audio2 = AVAudioPlayer()
var quote: Quote? {
didSet {
guard let quoteName = quote?.name else {return}
button1.setTitle("\(quoteName)", for: .normal)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
setCellShadow()
}
func setCellShadow() {
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 1)
self.layer.shadowOpacity = 1
self.layer.shadowRadius = 1.0
self.layer.masksToBounds = false
self.clipsToBounds = false
self.layer.cornerRadius = 3
}
func setup() {
self.backgroundColor = UIColor.lightGray
self.addSubview(button1)
do {
let audioPlayer1 = Bundle.main.path(forResource: "blaster-firing", ofType: "wav")
try audio1 = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPlayer1!))
} catch {
//ERROR
}
do {
let audioPlayer2 = Bundle.main.path(forResource: "yodalaughing", ofType: "wav")
try audio2 = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPlayer2!))
} catch {
}
// button setups
button1.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0)
button1.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
}
// button creations
let button1: UIButton = {
let button = UIButton()
button.tag = 0
button.layer.cornerRadius = 5
button.layer.borderColor = UIColor.black.cgColor
button.layer.borderWidth = 1
button.setTitle("", for: .normal)
button.setTitleColor(.cyan, for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
button.frame = CGRect(x: 50, y: 50, width: 150, height: 150)
return button
}()
@IBAction func buttonClicked(sender: UIButton!) {
// print(quote!.name! as Any)
if (sender.tag == 0) {
audio1.play()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
struct Quote {
let name: String?
}
答案 0 :(得分:2)
一些建议。
首先,我建议避免使用标记进行参考操作。虽然从技术上讲可以正常工作,但管理和跟踪起来可能会有些困难。
第二,我将音频管理放在您的单元之外。该单元可以跟踪需要播放哪种音频,但是让它触发其他地方的声音。
例如,我可能会以这种方式处理您的应用。
让我们定义我们要播放的音频。
enum AudioClip {
case blaster
case yoda
}
如果此音板应用程序将提供用户创建自己的音板,则您可能需要构建其他数据源。但是对于此示例,这将起作用。我会将其添加到您的Quote
结构中。实际上,您可以在枚举中将剪辑的名称定义为计算属性,然后替换Quote结构。
然后,构建一个AudioManager
,它可以容纳AudioClip
盒,并为其播放特定的音频剪辑。
这简化了您的单元,因此不需要管理不同音频片段的逻辑。实际上,它只需要知道该特定单元格的Quote
。轻按该单元格后,该单元格或集合视图都可以告诉AudioManager
播放特定的音频剪辑。
答案 1 :(得分:1)
由于您正在使用可重用的集合视图单元,因此足以在每个单元中创建一个音频播放器并将声音文件名传递给它。 试试这个选项:
在cellForItemAtIndexPath中,将声音文件名称以及引用传递给单元格:
cell.quote = quotes[indexPath.item]
cell.soundName = <#yourSoundName#>
在按钮处理程序方法中,仅执行audio1.play()而不进行标签检查:
@IBAction func buttonClicked(sender: UIButton!) {
audio1.play()
}
答案 2 :(得分:1)
要回答您的问题,不。一个按钮只有一个标签。
正如其他人所建议的那样,按钮标记是一种很脆弱的方法,可以确定所敲击的声音。
最好弄清楚单元格映射到哪个indexPath并使用它来查找数据模型中的信息。您可以获取按钮的坐标,将其转换为集合视图的坐标系,然后使用集合视图方法indexPathForItem(at:)
来确定点击了哪个indexPath。
我也同意乔希(Josh)的观点,即您不应将声音逻辑放在牢房中。让按钮动作在视图控制器中调用目标/动作,您可以在其中使用发送方找出按钮所属的indexPath,在模型中查找该声音,然后使用声音管理器对象播放声音。>
答案 3 :(得分:1)
正如其他人指出的那样,在一个按钮上使用多个标签可能不是编程上的好主意。但是,尽管有人说无法做到,但还是可以创建类似的东西。创建一个新的按钮类
java.lang.RuntimeException: Unable to start activity ComponentInfo{ma.ac.iav.menunaviagtion/ma.ac.iav.menunavigation.testanat.JustCoverFlowActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
at com.github.bleeding182.recyclerviewdecorations.viewpager.LinePagerIndicatorDecoration.<init>(LinePagerIndicatorDecoration.java:55)
at ma.ac.iav.menunavigation.testanat.JustCoverFlowActivity.onCreate(JustCoverFlowActivity.java:57)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
尽管那不是您要尝试的。单元格中创建的每个按钮都是其自己的对象。因此,您可以将每个按钮设置为自己的标签,而不是一个需要多个标签的按钮。如果您将indexPath.row的单元格设置为1到3,则每个其他按钮仍将带有标签0
但是将每个按钮设置为特定声音的另一种方法是这样的:在您的收藏夹中的收藏夹视图中
class ButtonWithMultipleTags: UIButton {
var tags : [int] = [ ]
}
这可能不是最好的方法,但是应该可以。我正在用手机,请检查是否有小错误
答案 4 :(得分:0)
为临时解决此问题,我使用了Brandons之类的版本。在我的cellForItemAt
中:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! SoundBoardCell
cell.quote = quotes[indexPath.item]
cell.button1.tag = indexPath.row // this is the added line
return cell
}
和我的buttonClicked
@IBAction func buttonClicked(sender: UIButton!) {
if (sender.tag == 0) {
audio1.play()
}
if (sender.tag == 1) {
audio2.play()
}
}
仍然希望使用其他可用方法进行清理,但这目前仍在起作用。当我添加其他wav文件时,将有很多行音频文件。