-4

I am currently working on code that disables a UIImageView while an AVAudioPlayer is playing. The AVAudioPlayer and the ImageView I want to disable are located in two different classes. I tried writing a delegate to have my two classes communicate. For some reason, the protocol method is never run even though my class conforms to the protocol, calling the method and setting the delegate to self. In my Card class, in the audioDidFinishPlaying method, I unwrap the delegate safely. When I run my code, the print statement is called every time instead of the isOn method. Can anyone offer any advice on this issue?

The code for the class containing my AVAudioPlayer is below

import Foundation; import UIKit; import AVFoundation

protocol isOnProtocol{
func isOn()
}

class Card: NSObject
{
    var delegate: isOnProtocol?
    var player: AVAudioPlayer?
    var image: UIImage
    var soundUrl: String

    init(image: UIImage, soundUrl: String, isActive:Bool = true) {
        self.image = image
        self.soundUrl = soundUrl

    }

    func playSound()
    {
        guard let url = Bundle.main.url(forResource: self.soundUrl, withExtension: "m4a") else { return }
        do
        {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)

            player = try AVAudioPlayer(contentsOf: url)
            //player?.delegate = self
            guard let player = player else { return }
            player.prepareToPlay()
            player.play()
            audioPlayerDidFinishPlaying(player)



            print("play")
        } catch let error {
            print(error.localizedDescription)
        }
    }
}

extension Card: AVAudioPlayerDelegate  {
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer){
        if let del = delegate{
            del.isOn()
        }else{
            print("the delegate is not set")
        }

    }
}

The code for my ViewController that I am trying to communicate with is below. When the image tapped function is called, the sound plays and UserInteractions should be false until the AVAudioPlayer is finished (isOn function is called).

import UIKit



class SecondViewController: UIViewController , UIGestureRecognizerDelegate, isOnProtocol  {



@IBAction func home(_ sender: Any) {
    performSegue(withIdentifier: "home", sender: self)
}

@IBOutlet weak var imgPhoto: UIImageView!


var imageIndex: Int = 0
var itemList:[Card] = []


func addlist(list:[String])
{
    for word in list
    {
        itemList.append(Card(image: UIImage(named: word)!, soundUrl: word))
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    for i in 0..<11 {
        let list: [String]
        switch i {
        case 0: list = ["lake", "lamb", "lamp", "lark", "leaf", "leash", "left", "leg", "lime", "lion", "lips", "list", "lock", "log", "look", "love", "lunch"]
        case 1: list = ["ladder", "ladybug", "laughing", "lawnmower", "lemon", "leopard", "leprechaun", "letters", "licking", "lifesaver", "lifting", "lightbulb", "lightning",
                 "listen", "llama"]
        case 2: list = ["alligator", "balance", "ballerina", "balloon", "bowling", "cello", "colors", "curlyhair", "dollar", "dolphin", "elephant", "eyelashes", "gasoline",

                 "goalie", "hula", "jellyfish", "olive", "pillow", "pilot", "polarbear", "rollerskate", "ruler", "silly", "telephone", "television", "tulip", "umbrella", "valentine",

                 "violin", "yellow", "xylophone"] //yellow xylophone
        case 3: list = ["apple", "ball", "bell", "bubble", "castle", "fall", "fishbowl", "girl", "owl", "pail", "peel", "pool", "smile", "whale", "wheel"] //whale wheel
        case 4: list = ["planet", "plank", "plant", "plate", "play", "plum", "plumber", "plus"]
        case 5: list = ["black", "blanket", "blender", "blocks", "blond", "blood", "blow", "blue"]
        case 6: list = ["flag", "flipflop", "float", "floor", "flower", "fluffy", "flute", "fly"]
        case 7: list = ["glacier", "glad", "glasses", "glide", "glitter", "globe", "glove", "glue"]
        case 8: list = ["clam", "clamp", "clap", "claw", "clean", "climb", "clip", "cloud"]
        case 9: list = ["sled", "sleep", "sleeves", "slice", "slide", "slime", "slip", "slow"]
        case 10: list = ["belt", "cold", "dolphin", "elf", "golf", "melt", "milk", "shelf"]
        default: fatalError()

        }
        if UserDefaults.standard.value(forKey: "\(i)") as? Bool ?? true {
        addlist(list:list)
        }
    }
   print(itemList.count)


    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
    imgPhoto.isUserInteractionEnabled = true
    imgPhoto.addGestureRecognizer(tapGestureRecognizer)


    imgPhoto.image =  itemList[0].image

    // Do any additional setup after loading the view.
    //imgPhoto.isUserInteractionEnabled = true

    let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(Swiped(gesture:)))
    leftSwipe.cancelsTouchesInView = false

    let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(Swiped(gesture:)))
    rightSwipe.cancelsTouchesInView = false

    leftSwipe.direction = .left
    rightSwipe.direction = .right

    view.addGestureRecognizer(leftSwipe)
    view.addGestureRecognizer(rightSwipe)

}



@IBAction func memoryButton(_ sender: Any) {
  performSegue(withIdentifier: "memory", sender: self)

}



func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
imgPhoto.isUserInteractionEnabled = false
let card = itemList[imageIndex]
card.delegate = self
card.playSound()
}
internal func isOn() {
    imgPhoto.isUserInteractionEnabled = true
}


func Swiped(gesture: UIGestureRecognizer) {
    imgPhoto.isUserInteractionEnabled = true

    if let swipeGesture = gesture as? UISwipeGestureRecognizer {

        switch swipeGesture.direction {

        case UISwipeGestureRecognizerDirection.right :
            print("User swiped right")

            // decrease index first

            imageIndex -= 1

            // check if index is in range

            if imageIndex < 0 {

                imageIndex = itemList.count - 1

            }
            UIImageView.transition(with: imgPhoto, duration: 0.3, options: .transitionFlipFromLeft, animations: nil, completion: nil)
            imgPhoto.image = itemList[imageIndex].image

        case UISwipeGestureRecognizerDirection.left:
            print("User swiped Left")

            // increase index first

            imageIndex += 1

            // check if index is in range

            if imageIndex > itemList.count - 1 {

                imageIndex = 0

            }
             UIImageView.transition(with: imgPhoto, duration: 0.3, options: .transitionFlipFromRight, animations: nil, completion: nil)
            imgPhoto.image = itemList[imageIndex].image
        default:


            break //stops the code/codes nothing.
        }
    }
}
}
redtomato
  • 70
  • 7
  • Why are you posting nearly the [same question](https://stackoverflow.com/questions/49453402/setting-delegate-to-self-throwing-error-in-swift) but under a new account? What's wrong with the answer you were given and accepted? The changes in this question's code is not what you were given in the other question's answer. – rmaddy Mar 23 '18 at 20:43
  • You call .playSound on itemList[imageIndex] but you don't show the code where itemList is build. Did you check with the debugger (and a breakpoint) that .delegate is set to something != nil before .playSound() is called? – D. Mika Mar 23 '18 at 20:47
  • @rmaddy I accepted the other answer because it solved the error that I had and did a good job explaining the issue. Now I have this issue. Also the code I posted here does reflect the changes given from the other answer does it not? – redtomato Mar 23 '18 at 20:50
  • What is `itemList` in `SecondViewController`? What debugging have you done? There's a lot of missing information in your question. – rmaddy Mar 23 '18 at 20:50
  • 1
    The local `card` variable in `viewDidLoad` goes out of scope at the end of `viewDidLoad` so it does nothing. – rmaddy Mar 23 '18 at 20:51

1 Answers1

0

Your card instance is not the card which is playing the sound, you can remove your card implementation in viewDidLoad, and you have to set the delegate in the card object that you get from your itemList array:

func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
    imgPhoto.isUserInteractionEnabled = false
    let card = itemList[imageIndex]
    card.delegate = self
    card.playSound()
}
Francesco Deliro
  • 3,899
  • 2
  • 19
  • 24
  • I believe that this does allow my protocol func to run. Unfortunately however my imgPhoto is never disabled, it remains enabled the whole time. Ive updated my question to include your answer and it now includes all of the code in my class if you could take another look. – redtomato Mar 23 '18 at 21:15
  • Never mind I got it working. Thank you for your answer! – redtomato Mar 23 '18 at 21:30