SwiftUI 和 UIKit 交互问题

问题描述 投票:0回答:1

我在将基本内容从 UIKit 导入到 SwiftUI 时遇到了严重的问题,这似乎剥夺了 SwiftUI 快速原型设计 UI 元素的所有优势。我将把我的问题分成不同的帖子,以遵守坚持一个问题的指导方针。

下面的代码通过一个名为

AVPlayerViewController
的中间
UIViewController
来呈现
VideoViewControllerWrapper
,它在
makeUIViewController
中返回。我使用中间控制器,因为否则
AVPlayerViewController
在启动时不会显示播放控件。

我的第一个问题是这个视图控制器应该在没有动画的情况下被关闭。无论如何我看不到实现它。

这就是我目前驳回它的方式。

 VideoPlayerView(player: player, showsPlaybackControls: true, onCompletion: { complete in
            shouldDismiss.toggle()
        })
         .ignoresSafeArea(.all)
         .frame(maxWidth: .infinity)
         .aspectRatio(16/9.0, contentMode: .fit)
         .onChange(of: shouldDismiss) { newValue in
             dismiss()
         }
import Foundation
import SwiftUI
import AVKit

public typealias VideoPlayerCompletion = (
    Bool
) -> Void

public struct VideoPlayerView: UIViewControllerRepresentable {
    
    let player: AVPlayer?
    var showsPlaybackControls: Bool
    var onCompletion:VideoPlayerCompletion?
  
    public init(player: AVPlayer?,
                showsPlaybackControls: Bool = true,
                onCompletion:VideoPlayerCompletion? = nil) {
        self.player = player
        self.showsPlaybackControls = showsPlaybackControls
        self.onCompletion = onCompletion
    }
    
    public func makeUIViewController(context: Context) -> VideoViewControllerWrapper {
        return VideoViewControllerWrapper(presentingController: false, player: player, onCompletion: onCompletion)
    }
    
    public func updateUIViewController(_ controller: VideoViewControllerWrapper, context: Context) {
        controller.player = player
        controller.onCompletion = onCompletion
      //  controller.updateState()
    }
   
    func dismantleUIViewController(_ uiViewController: UIViewController, coordinator: ()) {
           print("Calling dismantle")
    }
}

public final class VideoViewControllerWrapper: UIViewController, UIViewControllerTransitioningDelegate {

    var presentingController = false
    var player:AVPlayer?
    var onCompletion:VideoPlayerCompletion?

    init(presentingController: Bool = false, player: AVPlayer? = nil, onCompletion: VideoPlayerCompletion? = nil) {
        self.presentingController = presentingController
        self.player = player
        self.onCompletion = onCompletion
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override public func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        updateState()
    }
    
    fileprivate func updateState() {
        
        if player != nil {
            if !presentingController {
                presentingController = true
                let controller = AVPlayerViewController()
                controller.player = player
                controller.modalPresentationStyle = .fullScreen
                controller.showsPlaybackControls = true
                controller.transitioningDelegate = self
                
                self.present(controller, animated: false, completion: {
                    
                })
               
            }
        }
    }
    
    public func animationController(forDismissed dismissed: UIViewController)
    -> UIViewControllerAnimatedTransitioning?
    {
        // The dismissal was before the movie ended
      //  print("AVPlayerController dismissed")
        onCompletion?(true)
        return nil
    }

}
ios swift swiftui uikit avplayerviewcontroller
1个回答
1
投票

这里是一些示例代码,请参阅注释以获取一些解释。

import SwiftUI
struct AVPlaverInterfaceView: View {
    @State private var isPresenting: Bool = false
    var body: some View {
        Button("present") {
            isPresenting.toggle()
        }
        .disabled(isPresenting) // You can't dismiss the player from here.
        .background {
            //It doesn't matter where this goes as long as it is somewhere in the `body`, this prevents the error you mentioned before about not being in the view hierarchy.
            AVPlayer_UI(url: URL(string: "https://v.ftcdn.net/06/51/32/56/700_F_651325695_ybJOFmDp9TSn9vfIvbP1bbcqIeHJ7vaO.mp4")!, showsPlaybackControls: true, isPresenting: $isPresenting)
            //The parent is empty, nothing to show
                .frame(width: 0, height: 0)
        }
    }
}

#Preview {
    AVPlaverInterfaceView()
}

import AVKit
struct AVPlayer_UI: UIViewControllerRepresentable{
    let url: URL
    let showsPlaybackControls: Bool
    @Binding var isPresenting: Bool
    func makeUIViewController(context: Context) -> some UIViewController {
        .init() //Parent UIViewController
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        guard isPresenting, uiViewController.presentedViewController == nil else {return} // Only `present` if `isPresenting`
        let player = AVPlayer(url: url)
        let controller = AVPlayerViewController()
        
        // Do whatever setup you need
        controller.player = player
        controller.showsPlaybackControls = showsPlaybackControls
        controller.delegate = context.coordinator
        
        // Setup to know when the AVPlayerViewController is dismissed
        controller.transitioningDelegate = context.coordinator
        context.coordinator.onDismiss = {
            isPresenting = false
        }
        //Now present the controller from UIKit, no need for sheet or fullScreenCover.
        uiViewController.present(controller, animated: true) {
            controller.player!.play()
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    class Coordinator: NSObject, AVPlayerViewControllerDelegate, UIViewControllerTransitioningDelegate {
        var onDismiss: (() -> Void)?
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            print(#function)
            onDismiss?()
            return .none
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.