You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让UIButton记住选中状态?儿童拼图App音乐按钮状态异常问题

问题描述

我正在开发一款面向儿童的拼图App,包含24个不同场景的小拼图。我通过单例类MusicHelper管理背景音乐,代码如下:

class MusicHelper { 
    static let sharedHelper = MusicHelper() 
    var audioPlayerBGM: AVAudioPlayer?
    
    func playBackgroundMusic() {
        let BGM = URL(fileURLWithPath: Bundle.main.path(forResource: "jigsawBGM", ofType: "wav")!) 
        do {
            audioPlayerBGM = try AVAudioPlayer(contentsOf:BGM as URL)
            audioPlayerBGM!.numberOfLoops = -1
            audioPlayerBGM!.prepareToPlay()
            audioPlayerBGM!.play()
            audioPlayerBGM!.volume = 0.1
        } catch {
            print("Cannot play the file")
        }
    }
    
    func pauseBackgroundMusic() {
        let BGM = URL(fileURLWithPath: Bundle.main.path(forResource: "jigsawBGM", ofType: "wav")!) 
        do {
            audioPlayerBGM = try AVAudioPlayer(contentsOf:BGM as URL)
            audioPlayerBGM!.pause()
        } catch {
            print("Cannot play the file")
        }
    }
}

为了避免打扰家长,我写了一段代码实现背景音乐静音功能:

@IBOutlet weak var muteMusicButton: UIButton!
@IBAction func muteMusic(_ sender: UIButton) {
    sender.isSelected = !sender.isSelected 

    if muteMusicButton.isSelected {
        MusicHelper.sharedHelper.pauseBackgroundMusic()
    } else {
        MusicHelper.sharedHelper.playBackgroundMusic()
    }
}

静音按钮muteMusicButton位于单独的视图控制器(类似应用的控制/设置窗口),是自定义按钮:默认状态显示“音乐开启”图片,选中状态显示“音乐关闭”图片。

问题:当该视图控制器被dismiss后,按钮会重置为默认状态。再次进入时,音乐仍处于暂停状态,但按钮显示“音乐开启”图片,需要点击两次才能重新播放音乐。

请问如何让按钮在视图控制器关闭/重新进入时记住选中状态?(可能用到UserDefaults,但我不太熟悉!)或者是否有其他不同的实现方式?


解决方案

首先要修复MusicHelper里的核心问题:你的pauseBackgroundMusic()方法每次都会重新创建新的AVAudioPlayer实例,而非暂停正在播放的那个,这会直接导致状态混乱,先修改这个基础问题:

class MusicHelper { 
    static let sharedHelper = MusicHelper() 
    var audioPlayerBGM: AVAudioPlayer?
    
    // 初始化时一次性加载音频资源,避免重复创建实例
    init() {
        guard let path = Bundle.main.path(forResource: "jigsawBGM", ofType: "wav") else {
            print("BGM文件未找到")
            return
        }
        let bgmURL = URL(fileURLWithPath: path)
        do {
            audioPlayerBGM = try AVAudioPlayer(contentsOf: bgmURL)
            audioPlayerBGM?.numberOfLoops = -1
            audioPlayerBGM?.volume = 0.1
            audioPlayerBGM?.prepareToPlay()
        } catch {
            print("初始化BGM播放器失败:\(error)")
        }
    }
    
    func playBackgroundMusic() {
        audioPlayerBGM?.play()
    }
    
    func pauseBackgroundMusic() {
        audioPlayerBGM?.pause()
    }
    
    // 新增方法:判断当前音乐是否处于播放状态
    func isMusicPlaying() -> Bool {
        return audioPlayerBGM?.isPlaying ?? false
    }
}

接下来用UserDefaults保存静音状态,同时在视图控制器加载时同步按钮状态:

步骤1:定义UserDefaults的键(统一管理避免拼写错误)

可以在视图控制器内部或者全局常量文件中定义:

private let kMusicMutedKey = "MusicMutedStatus"

步骤2:修改按钮点击事件,同步保存状态

@IBAction func muteMusic(_ sender: UIButton) {
    sender.isSelected = !sender.isSelected 
    // 将状态保存到UserDefaults
    UserDefaults.standard.set(sender.isSelected, forKey: kMusicMutedKey)
    
    if sender.isSelected {
        MusicHelper.sharedHelper.pauseBackgroundMusic()
    } else {
        MusicHelper.sharedHelper.playBackgroundMusic()
    }
}

步骤3:在视图控制器显示时同步按钮状态

每次进入设置页面时,从UserDefaults读取保存的状态并设置按钮:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // 读取保存的状态,默认值为false(即未静音、音乐开启)
    let isMuted = UserDefaults.standard.bool(forKey: kMusicMutedKey)
    muteMusicButton.isSelected = isMuted
}

额外优化:App启动时自动恢复音乐状态

在SceneDelegate的启动方法中,根据保存的状态自动恢复音乐播放状态:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let isMuted = UserDefaults.standard.bool(forKey: kMusicMutedKey)
    if !isMuted {
        MusicHelper.sharedHelper.playBackgroundMusic()
    }
}

这样处理后,无论关闭再重新进入设置页面,按钮都会正确匹配当前的音乐状态,不会出现需要点击两次才能播放的问题。

内容的提问来源于stack exchange,提问作者KrisP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:58:17