gpt4 book ai didi

binding - 如何在 SwiftUI 中控制 AVPlayer?

转载 作者:行者123 更新时间:2023-12-05 09:12:16 25 4
gpt4 key购买 nike

在我的新 SwiftUI 项目中,我有一个用于从 url 流式传输音乐的 AVPlayer。现在我需要通过 slider 控制当前播放轨道的时间和音量,这里是设计部分:

enter image description here

现在我可以使用 UserData 最终类及其 @Published 变量来控制播放器,例如 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, duration 我只能从playerplayer?.currentItem ,所以我不能像这样制作 slider :

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

我怎样才能控制这些东西?

最佳答案

我没有找到任何解决方案,所以尝试自己解决。学习了一点Combine框架,继承了AVPlayer类,在ObservableObject协议(protocol)下签名,使用了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)

}

}

关于binding - 如何在 SwiftUI 中控制 AVPlayer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58779184/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com