Search code examples
iosswiftswiftuiios13airplay

How to display the AirPlay Menu SwiftUI


Thanks to the airplay audio systemName emoji I made a nice icon

Button(action: {
        showAirplay()
}, label: {
    Image(systemName: "airplayaudio")
        .imageScale(.large)
})

func showAirplay() {
       ???
}

But I have no idea how to display the famous menu :


Solution

  • Finally I managed to solve it myself :D Like said in the comment I have to "embed it inside" UIKit and use it in SwiftUI

    Firstly :

    struct AirPlayButton: UIViewControllerRepresentable {
        func makeUIViewController(context: UIViewControllerRepresentableContext<AirPlayButton>) -> UIViewController {
            return AirPLayViewController()
        }
    
        func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<AirPlayButton>) {
    
        }
    }
    
    

    Then classic ViewController where we know from ages how to show this famous AirPlay menu popup :

    class AirPLayViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let isDarkMode = self.traitCollection.userInterfaceStyle == .dark
    
            let button = UIButton()
            let boldConfig = UIImage.SymbolConfiguration(scale: .large)
            let boldSearch = UIImage(systemName: "airplayaudio", withConfiguration: boldConfig)
    
            button.setImage(boldSearch, for: .normal)
            button.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
            button.backgroundColor = .red
            button.tintColor = isDarkMode ? .white : .black
    
            button.addTarget(self, action: #selector(self.showAirPlayMenu(_:)), for: .touchUpInside)
            self.view.addSubview(button)
        }
    
        @objc func showAirPlayMenu(_ sender: UIButton){ // copied from https://stackoverflow.com/a/44909445/7974174
            let rect = CGRect(x: 0, y: 0, width: 0, height: 0)
            let airplayVolume = MPVolumeView(frame: rect)
            airplayVolume.showsVolumeSlider = false
            self.view.addSubview(airplayVolume)
            for view: UIView in airplayVolume.subviews {
                if let button = view as? UIButton {
                    button.sendActions(for: .touchUpInside)
                    break
                }
            }
            airplayVolume.removeFromSuperview()
        }
    }
    
    

    Finally in SwiftUI simply call :

    struct ContentView: View {
        var body: some View {
            VStack {
                Text("Hello World")
                AirPlayButton().frame(width: 40, height: 40) // (important to be consistent with this frame, like that it is nicely centered... see button.frame in AirPlayViewController)
    
            }
        }
    }