The Util Designer
SwiftUI 通過UIViewControllerRepresentable protocol來封裝AVPlayerViewController
xcode 13.4.1, swift 5.5, iOS 15.4
2022-09-09
SwiftUI雖然已經提供了很多內建的元件,但SwiftUI還沒有UIKit成熟,所以有些時候需要借助UIKit來實現,要做這個工作,就要通過SwiftUI提供的UIViewControllerRepresentable protocol來與UIKit中的視圖控制器 (View Controller)作橋樑溝通,這次試試把AVPlayerViewController打包成View來在SwiftUI里面使用。
1. 在itune上選一首自己喜歡的preview的歌,我這里選了這首 Light Year的歌,並將其下載及放到project中,並使用 SwiftUI 使用Bundle讀取已預先在Project中檔案的方法來創建URL:
2. 使用一個song class來封裝,將一個檔案轉成一個URL,以便後面容易的使用。
class Song {
    let songName : String
    let songExtension : String
    var songUrl : URL?
    
    init( _ songName : String , _ songExtension : String ) {
        self.songName = songName
        self.songExtension = songExtension
        guard let soundPath = Bundle.main.path(forResource: "LightYear", ofType: "m4a") else {
            return
        }
        self.songUrl = URL(fileURLWithPath: soundPath)
    }
}
3. 實現一個UIViewControllerRepresentable的View AudioPlayerView,在makeUIViewController方法中,創建立返回AVPlayerViewController,就實現使用AVPlayerViewController的橋梁:
import SwiftUI
import AVKit

struct AudioPlayerView : UIViewControllerRepresentable {
    
    let url : URL
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let player = AVPlayer(url: url)
        let viewController = AVPlayerViewController()
        viewController.player = player
        viewController.entersFullScreenWhenPlaybackBegins = true
        player.play()
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
        
    }
}
4. 最後就是來看看如何使用AudioPlayerView,以下程式就是按下play的按鈕,就顯示AudioPlayerView並自動播放音樂:
import SwiftUI
import AVKit

struct AudioPlayerView : UIViewControllerRepresentable {
    
    let url : URL
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let player = AVPlayer(url: url)
        let viewController = AVPlayerViewController()
        viewController.player = player
        viewController.entersFullScreenWhenPlaybackBegins = true
        player.play()
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
        
    }
}

class Song {
    let songName : String
    let songExtension : String
    var songUrl : URL?
    
    init( _ songName : String , _ songExtension : String ) {
        self.songName = songName
        self.songExtension = songExtension
        guard let soundPath = Bundle.main.path(forResource: "LightYear", ofType: "m4a") else {
            return
        }
        self.songUrl = URL(fileURLWithPath: soundPath)
    }
}

struct AudioPlayerExample: View {
    
    @State var playFlag = false
    let song = Song("LightYear", "m4a")
    
    var body: some View {
        Button(action: {
            if self.song.songUrl != nil {
                self.playFlag = true
            }
        }, label: {
            Text("Play")
        })
        .sheet(isPresented: $playFlag) {
            AudioPlayerView(url : self.song.songUrl!)
        }
    }
}