不能在属性初始值设定项中使用实例成员“videoName”;属性初始值设定项在 'self' 可用之前运行

时间:2021-07-26 16:05:59

标签: swiftui model video-player

我对 SwiftUI 很陌生,当我尝试使用 Model 中的 videoName 显示视频时遇到了这个问题。 在 player = AVPlayer(...)(第 4 行)中,我想使用 Model 中的 videoName,而不是通过字符串“squats”查找资源。如果我更换它们,我会收到错误

<块引用>

不能在属性初始值设定项中使用实例成员“videoName”;属性初始值设定项在 'self' 可用之前运行

有人可以帮我吗?

这是我的代码:

struct ExercisingSessionView: View {
    
    let exerciseName: String
    let videoName: String
    
    @State var player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "squats", ofType: "mov")!))
    @State var isplaying = false
    @State var showcontrols = false
    
    var body: some View {
        
        CustomVideoPlayer(player: $player)
            .frame(width: 390, height: 219)
            .onTapGesture {
                self.showcontrols = true
                
            }
    }
    
    struct CustomVideoPlayer : UIViewControllerRepresentable {
        
        @Binding var player: AVPlayer
        
        func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
            
            let controller = AVPlayerViewController()
            controller.player = player
            controller.showsPlaybackControls = false
            return controller
        }
        
        func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
            
        }
    }
}

3 个答案:

答案 0 :(得分:0)

选项 1:

为您的 View 创建一个初始化程序,用于创建您的 @State 初始值:

struct ExercisingSessionView: View {
    
    let exerciseName: String
    let videoName: String
    
    @State var player : AVPlayer
    @State var isplaying = false
    @State var showcontrols = false
    
    init(exerciseName: String, videoName: String) {
        self.exerciseName = exerciseName
        self.videoName = videoName
        self._player = State(initialValue: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoName, ofType: "mov")!)))
    }
    
    var body: some View {
        CustomVideoPlayer(player: $player)
            .frame(width: 390, height: 219)
            .onTapGesture {
                self.showcontrols = true
            }
    }
}

这样做的缺点是,如果 ExercisingSessionView 经常被初始化(即使它实际上没有被重新渲染到视图层次结构中),你在 init 内部做了繁重的工作,这是对于性能来说,这通常是一个非常糟糕的主意。

选项 2:

player 声明为可选并在 onAppear 中加载初始值:



struct ExercisingSessionView: View {
    
    let exerciseName: String
    let videoName: String
    
    @State var player : AVPlayer?
    @State var isplaying = false
    @State var showcontrols = false
    
    var body: some View {
        Group {
            if let player = player {
                CustomVideoPlayer(player: player)
                    .frame(width: 390, height: 219)
                    .onTapGesture {
                        self.showcontrols = true
                    }
            }
        }.onAppear {
            player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoName, ofType: "mov")!))
        }
    }
}

struct CustomVideoPlayer : UIViewControllerRepresentable {
    var player: AVPlayer
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
        
        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
        
    }
}

这避免了选项 1 中的问题,因为 onAppear 只会被调用一次。

请注意,在这里,我将 player 内的 CustomVideoPlayer 设为常规的非绑定属性——因为 AVPlayer 是一个类,通过引用传递,因此没有理由拥有一个@Binding

答案 1 :(得分:0)

您可以将您的名字传递给您的自定义视图。

struct ExercisingSessionView: View {
    
    let exerciseName: String
    let videoName: String
    
    @State var isplaying = false
    @State var showcontrols = false
    
    var body: some View {
        
        CustomVideoPlayer(player: videoName)
            .frame(width: 390, height: 219)
            .onTapGesture {
                self.showcontrols = true
                
            }
    }
    
    struct CustomVideoPlayer : UIViewControllerRepresentable {
        
        let videoName: String
        
        func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
            
            let controller = AVPlayerViewController()
            controller.player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoName, ofType: "mov")!))
            controller.showsPlaybackControls = false
            return controller
        }
}

答案 2 :(得分:0)

我假设错误发生在这一行,当您将 squats 替换为 videoName 时:

@State var player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "squats", ofType: "mov")!))

您收到错误的原因是当您调用这行代码时未定义 videoName

要解决此问题,您可以在您定义的自定义 init 方法中设置您的属性。像这样:

    let videoName: String
    @State var player: AVPlayer
    
    init(exerciseName: String, videoName: String) {
        self.exerciseName = exerciseName
        self.videoName = videoName
        self.player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoName, ofType: "mov")!))
    }

请记住,您必须添加