如何在SwiftUI中控制AVPlayer? [英] How to control AVPlayer in SwiftUI?

查看:17
本文介绍了如何在SwiftUI中控制AVPlayer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的新SwiftUI项目中,我有一个AVPlayer,用于从url流媒体音乐。现在我需要通过滑块控制当前播放曲目的时间和音量,设计部分如下:

现在我可以使用Userdata最终类及其@发布的变量来控制播放器,如isPlaying

final class UserData: ObservableObject {
    // ...
    @Published var player: AVPlayer? = nil
    @Published var isPlaying: Bool = false
    //...

    func playPausePlayer(withSong song: Song, forPlaylist playlist: [Song]?) {
        //...
        if isPlaying {
            player?.pause()
        } else {
            player?.play()
        }

        isPlaying.toggle()
    }

}

很高兴知道这一部分是否有更好的决定🧐

问题是属性CurrentTime持续时间我只能从playerplayer?.currentItem获取,所以我不能制作这样的滑块:

@EnvironmentObject var userData: UserData
// ...
Slider(value: userData.player?.currentItem?.currentTime()!, in: 0...userData.player?.currentItem?.duration as! Double, step: 1)

我如何控制这些东西?

推荐答案

我没有找到任何解决方案,所以尝试自己解决。我学习了一点Combine框架,继承了AVPlayer类,并在ObservableObject协议下对其进行了签名,并使用了KVO。也许这不是最好的解决方案,但它是有效的,希望有人能给我一些建议,以便在未来改进代码。以下是一些代码片段:

import Foundation
import AVKit
import Combine

final class AudioPlayer: AVPlayer, ObservableObject {

    @Published var currentTimeInSeconds: Double = 0.0
    private var timeObserverToken: Any?
    // ... some other staff

    // MARK: Publishers
    var currentTimeInSecondsPass: AnyPublisher<Double, Never>  {
        return $currentTimeInSeconds
            .eraseToAnyPublisher()
    }

    // in init() method I add observer, which update time in seconds
    override init() {
        super.init()
        registerObserves()
    }

    private func registerObserves() {

        let interval = CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        timeObserverToken = self.addPeriodicTimeObserver(forInterval: interval, queue: .main) {
            [weak self] _ in
            self?.currentTimeInSeconds = self?.currentTime().seconds ?? 0.0
        }

    } 

    // func for rewind song time
    func rewindTime(to seconds: Double) {
        let timeCM = CMTime(seconds: seconds, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        self.seek(to: timeCM)
    }

    // sure I need to remove observer:
    deinit {

        if let token = timeObserverToken {
            self.removeTimeObserver(token)
            timeObserverToken = nil
        }

    }

}

// simplified slider 

import SwiftUI

struct PlayerSlider: View {

    @EnvironmentObject var player: AudioPlayer
    @State private var currentPlayerTime: Double = 0.0
    var song: Song // struct which contains the song length as Int

    var body: some View {

        HStack {

            GeometryReader { geometry in
                Slider(value: self.$currentPlayerTime, in: 0.0...Double(self.song.songLength))
                    .onReceive(self.player.currentTimeInSecondsPass) { _ in
                    // here I changed the value every second
                        self.currentPlayerTime = self.player.currentTimeInSeconds
                }
                // controlling rewind
                .gesture(DragGesture(minimumDistance: 0)
                .onChanged({ value in
                    let coefficient = abs(Double(self.song.songLength) / Double(geometry.size.width))
                    self.player.rewindTime(to: Double(value.location.x) * coefficient)
                }))
            }
            .frame(height: 30)

        }

    }

}


VolumeView更新 对于音量控制,我创建了新的UIViewRepresentable结构:

import SwiftUI
import UIKit
import MediaPlayer

struct MPVolumeViewRepresenter: UIViewRepresentable {


    func makeUIView(context: Context) -> MPVolumeView {

        let volumeView = MPVolumeView()
        volumeView.showsRouteButton = false // TODO: 'showsRouteButton' was deprecated in iOS 13.0: Use AVRoutePickerView instead.
        if let sliderView = volumeView.subviews.first as? UISlider {
        // custom design colors
            sliderView.minimumTrackTintColor = UIColor(red: 0.805, green: 0.813, blue: 0.837, alpha: 1)
            sliderView.thumbTintColor = UIColor(red: 0.805, green: 0.813, blue: 0.837, alpha: 1)
            sliderView.maximumTrackTintColor = UIColor(red: 0.906, green: 0.91, blue: 0.929, alpha: 1)
        }

        return volumeView

    }

    func updateUIView(_ uiView: MPVolumeView, context: UIViewRepresentableContext<MPVolumeViewRepresenter>) {
        // nothing here. really, nothing
    }

}

// and you can use it like:
struct VolumeView: View {

    var body: some View {

        HStack(alignment: .center) {
            Image("volumeDown")
                .renderingMode(.original)
                .resizable()
                .frame(width: 24, height: 24)

                MPVolumeViewRepresenter()
                    .frame(height: 24)
                    .offset(y: 2) // centering

            Image("volumeUp")
                .renderingMode(.original)
                .resizable()
                .frame(width: 24, height: 24)

        }.padding(.horizontal)

    }

}

这篇关于如何在SwiftUI中控制AVPlayer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆