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

SwiftUI Live Activities无法显示SpriteView问题求助

SpriteView在Live Activities(Dynamic Island)中无法正常显示的问题

我在主App中使用SpriteView显示精灵动画完全正常,但将其添加到Live Activities(Dynamic Island)后出现显示异常,日志无执行错误。相关代码片段如下:

var characterView: some View {
    SpriteView(scene: characterScene)
        .frame(width: 50, height: 50)
}

以下是完整的最小化代码示例:

Character Animation 代码

import Foundation
import SpriteKit

class CharacterScene: SKScene {
    let characterName: String
    init(characterName: String) {
        self.characterName = characterName
        super.init(size: CGSize(width: 300, height: 400))
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    var textures: [SKTexture] = []
    override func didMove(to view: SKView) {
        let atlas = SKTextureAtlas(named: characterName)
        textures = atlas.textureNames.sorted().map { atlas.textureNamed($0) }
        
        let sprite = SKSpriteNode(texture: textures[0])
        let maxSize: CGFloat = 200
        let aspectRatio = sprite.size.height / sprite.size.width
        sprite.size = CGSize(width: min(sprite.size.width, maxSize), height: min(sprite.size.height, maxSize * aspectRatio))
        sprite.position = CGPoint(x: frame.midX, y: frame.midY)
        addChild(sprite)
        
        let animation = SKAction.animate(with: textures, timePerFrame: 0.5)
        sprite.run(SKAction.repeatForever(animation))
    }
}

Widget 代码

import WidgetKit
import SwiftUI
import ActivityKit
import SpriteKit

struct CharacterIslandWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(
            for: CharacterIslandAttributes.self,
            content: { context in
                CharacterIslandWidgetView(context: context)
            },
            dynamicIsland: { context in
                DynamicIsland(
                    expanded: {
                        DynamicIslandExpandedRegion(.center) {
                            Text(context.state.CharacterName)
                        }
                        DynamicIslandExpandedRegion(.leading) {
                            SpriteView(scene: CharacterScene(characterName: context.state.CharacterImage))
                                .frame(width: 50, height: 50)
                        }
                    },
                    compactLeading: {
                        SpriteView(scene: CharacterScene(characterName: context.state.CharacterImage))
                            .frame(width: 50, height: 50)
                    },
                    compactTrailing: {
                    },
                    minimal: {
                        SpriteView(scene: CharacterScene(characterName: context.state.CharacterImage))
                            .frame(width: 50, height: 50)
                    })
            }
    )}
}
struct CharacterIslandWidgetView: View {
    let context: ActivityViewContext<CharacterIslandAttributes>
    var body: some View {
        SpriteView(scene: CharacterScene(characterName: context.state.CharacterImage))
            .frame(width: 50, height: 50)
        Text(context.state.CharacterImage)
    }
}

问题补充

图集名称正确,资源文件夹已在App和Widget间共享,context.state.CharacterImage为包含所有精灵的图集文件夹名称。尝试过硬编码名称、查看错误日志均无结果,想确认SpriteView是否不允许在Live Activities(Dynamic Island)中使用,以及对应的解决办法。


问题原因与解决方案

核心原因

SpriteKit(包括SpriteView)目前不支持在Live Activities/Dynamic Island中使用。苹果的WidgetKit和ActivityKit框架对Live Activities可使用的视图类型有严格限制,所有基于UIKit/AppKit的封装视图(包括SpriteView)都无法在Live Activities环境中正常渲染。

替代方案1:SwiftUI原生逐帧动画

将精灵图集的图片拆分为单独序列帧,用SwiftUI的Timer和Image实现逐帧动画:

struct AnimatedCharacterView: View {
    let frameNames: [String] // 图集内所有图片的名称数组
    @State private var currentFrameIndex = 0
    
    var body: some View {
        Image(frameNames[currentFrameIndex])
            .resizable()
            .scaledToFit()
            .frame(width: 50, height: 50)
            .onAppear {
                // 每0.5秒切换一帧,循环播放
                Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
                    currentFrameIndex = (currentFrameIndex + 1) % frameNames.count
                }
            }
    }
}

使用时,传入图集的图片名称数组即可,需确保这些图片资源已添加到Widget的Target Membership中。

替代方案2:预渲染视频+VideoPlayer播放

将精灵动画预渲染为循环短视频(如MP4格式),在Live Activities中用VideoPlayer播放:

import AVKit

struct AnimatedCharacterVideoView: View {
    private let videoURL: URL
    
    init(videoName: String) {
        // 从Widget资源中获取视频URL
        self.videoURL = Bundle.main.url(forResource: videoName, withExtension: "mp4")!
    }
    
    var body: some View {
        VideoPlayer(player: createLoopPlayer())
            .frame(width: 50, height: 50)
            .scaledToFit()
    }
    
    private func createLoopPlayer() -> AVPlayer {
        let player = AVPlayer(url: videoURL)
        // 设置播放完成后循环
        player.actionAtItemEnd = .none
        NotificationCenter.default.addObserver(
            forName: .AVPlayerItemDidPlayToEndTime,
            object: player.currentItem,
            queue: .main
        ) { _ in
            player.seek(to: .zero)
            player.play()
        }
        player.play()
        return player
    }
}

需提前将动画转成视频文件,并确保视频资源已加入Widget的Target Membership。

关键注意事项

  • Live Activities仅支持有限的原生SwiftUI组件,例如Text、Image、ProgressView、VideoPlayer等,第三方或封装视图无法正常工作。
  • 所有资源(图片/视频)必须在Xcode中勾选Widget的Target Membership,否则Widget无法访问到资源。

内容的提问来源于stack exchange,提问作者Wamy-Dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 19:14:54