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

UIMenuElement状态存UserDefaults的代码优化及APP退出前清数据方案

问题分析与优化方案

一、现有存储方式的问题

你的代码直接将UIMenuElement.State枚举实例存入UserDefaults,这种方式是错误的:

  • UserDefaults无法直接存储自定义枚举类型(即使是系统枚举,也需要通过其rawValue来存储,因为UIMenuElement.State遵循RawRepresentable协议,底层是Int类型)。
  • 读取时的强制转换as! UIMenuElement.State会在首次启动(无存储数据)或数据类型不匹配时直接崩溃。

二、代码优化点

  1. 统一UserDefaults键名:用常量替代硬编码的键名,避免拼写错误。
  2. 正确存储枚举值:存储UIMenuElement.State的rawValue(Int类型),读取时通过rawValue初始化枚举并设置默认值。
  3. 修复菜单状态初始化逻辑:初始时根据存储的状态,正确设置两个UIAction的选中状态,而非统一设为同一个值;移除手动设置action.state的冗余代码(因为菜单开启了.singleSelection选项,会自动管理选中状态)。
  4. 移除冗余代码:删除重复创建subtitlesMenu的代码。
  5. 安全解包与默认值:读取UserDefaults时避免强制解包,使用可选绑定或默认值。

三、优化后的代码

// 定义UserDefaults键名常量
private extension UserDefaults {
    static let captionStateKey = "captionState"
}

private func addMenuControlForSubtitles() {
    // 读取存储的状态,默认值为.on(可根据需求调整)
    let savedRawValue = UserDefaults.standard.integer(forKey: UserDefaults.captionStateKey)
    let savedState = UIMenuElement.State(rawValue: savedRawValue) ?? .on
    
    let onAction = UIAction(title: Constants.Playback.subtitlesOnTitle, state: savedState == .on ? .on : .off) { [weak self] _ in
        self?.subtitlesLabel.isHidden = false
        // 存储选中状态的rawValue
        UserDefaults.standard.set(UIMenuElement.State.on.rawValue, forKey: UserDefaults.captionStateKey)
    }
    
    let offAction = UIAction(title: Constants.Playback.subtitlesOffTitle, state: savedState == .off ? .on : .off) { [weak self] _ in
        self?.subtitlesLabel.isHidden = true
        // 存储选中状态的rawValue
        UserDefaults.standard.set(UIMenuElement.State.off.rawValue, forKey: UserDefaults.captionStateKey)
    }

    // 创建子菜单,启用单选
    let submenu = UIMenu(title: Constants.Playback.subtitlesMenuTitle,
                         options: [.displayInline, .singleSelection],
                         children: [onAction, offAction])

    let captions = UIImage(systemName: "captions.bubble")
    subtitlesMenu = UIMenu(title: Constants.Playback.productCardMenuTitle, image: captions, children: [submenu])
    
    // 初始化字幕显示状态(根据存储的状态)
    subtitlesLabel.isHidden = savedState == .off

    #if os(tvOS)
    if let subtitlesMenu = subtitlesMenu {
        playerViewController.transportBarCustomMenuItems.append(subtitlesMenu)
    }
    #endif
}

四、应用退出前清除数据的实现

在AppDelegate中实现applicationWillTerminate方法,清除对应的UserDefaults数据:

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func applicationWillTerminate(_ application: UIApplication) {
        // 清除字幕状态存储
        UserDefaults.standard.removeObject(forKey: UserDefaults.captionStateKey)
    }
}

如果你的项目使用SceneDelegate,则在sceneDidEnterBackground中处理(iOS 13+多场景应用中,applicationWillTerminate可能不会触发,退到后台时清除更稳妥):

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    func sceneDidEnterBackground(_ scene: UIScene) {
        UserDefaults.standard.removeObject(forKey: UserDefaults.captionStateKey)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:05:20