Here's a sample code snippet for a video player in SwiftUI, where the player's core is implemented in UIKit (specifically in PlayerViewController
):
struct Page: View {
let delegate = Delegate()
@State private var isPlay = true
var body: some View {
VStack {
PlayerView(delegate: delegate)
Button("Play") { delegate.play() }
}
}
}
class Delegate {
var controller: PlayerViewController? = nil
func play() { controller?.play() }
func pause() { controller?.pause() }
}
struct PlayerView: UIViewControllerRepresentable {
let delegate: Delegate
func makeUIViewController(context: Context) -> some UIViewController {
let c = PlayerViewController()
delegate.controller = c
return c
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
}
class PlayerViewController: UIViewController {
func play() { ... }
func pause() { ... }
}
In the code above, the page correctly displays the PlayerViewController
, and the video plays without issue. However, I have run into a problem when trying to call functions like play
or pause
on the video.
I created a delegate (the Delegate
class) to encapsulate these functions, passing the delegate to PlayerView
, and assigned delegate.controller = c
in the makeUIViewController
method.
Unfortunately, when I tap the play button, I discovered that the controller
is nil.
I'm puzzled as to why this delegate isn't functioning as expected. Can anyone help me understand what's going wrong, and guide me on how to fix it? Any assistance would be greatly appreciated!
Additionally, I replicated the same scenario in a playground, and everything appears to be working as expected:
class City {
let name: String
init(name: String) { self.name = name }
func say() { print("hello I am \(name)") }
}
class Delegate {
var city: City? = nil
func sayFromDelegate() {
city?.say()
}
}
struct Person {
let name: String = "Person"
let delegate: Delegate
func foo() {
let c = City(name: "QingDao")
delegate.city = c
}
}
let d = Delegate()
let p = Person(delegate: d)
p.foo() // force bind delegate
d.sayFromDelegate() // hello I am QingDao
View structs are ephemeral so can't hold onto objects so your let delegate = Delgate()
is a memory leak.
You need to either pass a closure or a binding to your UIViewControllerRepresentable and implement the update func to update the view controller if these values have changed from the last time update was called.
Eg
@Binding var myBinding : Int
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
uiViewController.someValueChanged = { x in
myBinding = x
}
}