简体   繁体   中英

Pass variable to SwiftUI AVPlayer from another view

Relative newbie here building iOS app using SwiftUI. Tried searching for a solution but no luck.

From my app home screen ("HomeView.swift"), when users select a thumbnail image of the video they want to play, the video's absolute URL on the web (which is retrieved from a local.json file) gets passed in the variable ("videoLink") to the video player view ("VideoPlayer.swift").

Right now, Xcode lets me successfully render the value of the videoLink variable if displayed simply as text, as in this:

Text("\(videoLink)")

And it allows a hard-coded URL to be used with AVPlayer, as in this:

let player = AVPlayer(url: URL(string: "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8")!)

However, Xcode gives an error when I try to use the variable value with AVPlayer in the following way:

let player = AVPlayer(url: URL(string: "\(videoLink)")!)

Here's the error message:

"Cannot use instance member 'videoLink' within property initializer; property initializers run before 'self' is available."

I'm including the complete code for the VideoPlayer.swift view here.

import SwiftUI
import AVKit

struct PlayerView: View {
    
    @ObservedObject var model = VideoModel()
    
    var videoLink = ""

    let player = AVPlayer(url: URL(string: "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8")!)

    var body: some View {

        VideoPlayer(player: player) {
            VStack {
                Text("\(videoLink)")
                    .foregroundColor(.white)
                Spacer()
            }
        }
            .onAppear() {
                player.play()
            }
            .onDisappear() {
                player.pause()
            }
    }
}

Any tips would be greatly appreciated!

There are a few ways to approach this.

One way is to make your player variable an optional @State variable and set its value inside onAppear :

struct PlayerView: View {
    
    @ObservedObject var model = VideoModel()
    
    var videoLink : String
    
    @State private var player : AVPlayer?
    
    var body: some View {
        
        VideoPlayer(player: player) {
            VStack {
                Text("\(videoLink)")
                    .foregroundColor(.white)
                Spacer()
            }
        }
        .onAppear() {
            guard let url = URL(string: videoLink) else {
                return
            }
            player = AVPlayer(url: url)
            player?.play()
        }
        .onDisappear() {
            player?.pause()
        }
    }
}

Another option (which I don't like quite as much) is to pass the player in as a parameter:

struct PlayerView: View {
    
    @ObservedObject var model = VideoModel()
    
    var videoLink : String
    var player : AVPlayer
    
    var body: some View {
        
        VideoPlayer(player: player) {
            VStack {
                Text("\(videoLink)")
                    .foregroundColor(.white)
                Spacer()
            }
        }
        .onAppear() {
            player.play()
        }
        .onDisappear() {
            player.pause()
        }
    }
}

and call it like:

PlayerView(videoLink: "url", player: AVPlayer(url: url))

One reason that I like the first method better is that in the 2nd option, if the parent view were re-rendered, it would also re-instantiate the AVPlayer , whereas in the first option, the heavy lifting is done in onAppear and then kept between renders with the @State variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM