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

SwiftUI解析BookmarkData编译失败及音频无限循环实现求助

问题解决方案

1. 解析书签的编译错误修复

报错Cannot pass immutable value as inout argument: literals are not mutable是因为bookmarkDataIsStale参数为inout类型,必须传入可变变量的引用,不能直接传字面量false。修改解析代码如下:

do {
    var isBookmarkStale = false
    // 传入可变变量的引用,替代字面量false
    musicUrl = try URL(resolvingBookmarkData: asana.musicBookmark, 
                      options: .withSecurityScope, 
                      relativeTo: nil, 
                      bookmarkDataIsStale: &isBookmarkStale)
    
    // 可选:如果书签过期,重新生成并保存
    if isBookmarkStale, let validUrl = musicUrl {
        do {
            asana.musicBookmark = try validUrl.bookmarkData(options: .withSecurityScope)
        } catch {
            print("Failed to refresh bookmark: \(error)")
        }
    }
    
    // 启动安全域访问(必须步骤)
    guard musicUrl.startAccessingSecurityScopedResource() else {
        print("Failed to access security-scoped resource")
        return
    }
    defer {
        musicUrl.stopAccessingSecurityScopedResource()
    }
    
    musicSound = try AVAudioPlayer(contentsOf: musicUrl)
    musicSound?.numberOfLoops = -1 // 无限循环
    musicSound?.play()
} catch {
    print("Failed to play Music: \(error.localizedDescription).")
}

2. iOS书签保存的权限问题

iOS中要生成跨启动有效的安全域书签,必须在调用bookmarkData()时指定.withSecurityScope选项,同时确保正确管理资源访问权限。修改保存代码:

.fileImporter(isPresented: $chooseMusic, allowedContentTypes: [.audio]) { result in
    switch result {
    case .success(let fileUrl):
        let hasAccess = fileUrl.startAccessingSecurityScopedResource()
        defer {
            if hasAccess {
                fileUrl.stopAccessingSecurityScopedResource()
            }
        }
        do {
            // 生成带安全域权限的书签
            asana.musicBookmark = try fileUrl.bookmarkData(
                options: .withSecurityScope,
                includingResourceValuesForKeys: nil,
                relativeTo: nil
            )
        } catch {
            print("Failed to create bookmark: \(error)")
        }
    case .failure(let error):
        print("File importer failed: \(error)")
    }
}
  • 使用defer确保stopAccessingSecurityScopedResource()一定会被调用,避免资源泄漏
  • 将allowedContentTypes改为.audio,更精准匹配音频文件
  • 必须添加.withSecurityScope选项,否则书签无法保留跨启动的访问权限

3. 音频无限循环与终止实现

  • 无限循环:将AVAudioPlayer的numberOfLoops设为-1(官方标准写法,比设大数更可靠)
  • 终止播放:在视图消失或用户点击停止按钮时,调用stop()并释放播放器实例:
struct YourMusicView: View {
    @State private var musicSound: AVAudioPlayer?
    // 其他状态变量...
    
    var body: some View {
        VStack {
            Button("停止播放") {
                musicSound?.stop()
                musicSound = nil // 释放资源,彻底终止
            }
            // 其他视图元素
        }
        .onDisappear {
            // 视图离开时自动停止播放
            musicSound?.stop()
            musicSound = nil
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:56:02