42

I have tried to addSubview a SwiftUI View to UIView. self.view.addSubview(contentView)

Error: Cannot convert value of type 'ContentView' to expected argument type 'UIView'

Kindly help me to implement this UI.

import UIKit
import SwiftUI

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.backgroundColor = UIColor.lightGray
        
        let contentView = ContentView()
        view.addSubview(contentView) // Error: Cannot convert value of type 'ContentView' to expected argument type 'UIView'
    }


}


struct ContentView: View {
    var body: some  View {
        Text("Hello world")
    }
    
}
shim
  • 9,289
  • 12
  • 69
  • 108
Kathiresan Murugan
  • 2,783
  • 3
  • 23
  • 44
  • `View` protocol _!=_ a `UIView` – Ashley Mills Jun 17 '19 at 13:42
  • Watch the *very* first part of Session 231: Integrating SwiftUI. You need to use `UIHostingController`. https://developer.apple.com/videos/play/wwdc2019/231/ –  Jun 17 '19 at 14:57
  • Sorry for the second comment. Since `UIHostingController is a `UIViewController` *and* you wish to use `addSubview`, you'll also need to learn how to use a child view controller (done in `UIKit`) and part of that involves adding the view as a subview to your parent view. The hosting controllers (one for UIKit, AppKit, and WatchKit) are full controllers, and are easily used for a *full screen* SwiftUI view. Hope this helps. –  Jun 17 '19 at 15:06

1 Answers1

57

Step 1: Create instances of UIHostingController by using SwiftUI View

struct ContentView : View {
    var body: some View {
        VStack {
            Text("Test")
            Text("Test2")

        }
    }
}

var child = UIHostingController(rootView: ContentView())

Step 2: Add instance of UIHostingController as a child view controller to Any UIKit ViewController

var parent = UIViewController()
child.view.translatesAutoresizingMaskIntoConstraints = false
child.view.frame = parent.view.bounds
// First, add the view of the child to the view of the parent
parent.view.addSubview(child.view)
// Then, add the child to the parent
parent.addChild(child)

You can use the following code to remove a child controller Remove from view Controller

// Then, remove the child from its parent
child.removeFromParent()

// Finally, remove the child’s view from the parent’s
child.view.removeFromSuperview()
Imran
  • 3,045
  • 2
  • 22
  • 33
  • Thanks for your answer, so now I can use SwiftUI in Mac statusbar app. But, you are setting the SwiftUI view's frame by the parent view. I want to do it vice versa, is there a way? And that child's frame will always be zeros unless it has not been set, as far I get. – Faruk Nov 11 '19 at 14:28
  • 5
    This answer is good, thank you. I know Apple is very big on "just rewrite your app from scratch" and "don't connect with any third-party code". But for people on Earth, it would have been nice if Apple just handled all the boilerplate code above invisibly. Then we can gradually switch out UIViews to Views. – William Entriken Nov 24 '19 at 18:58
  • 16
    The above doesn't demonstrate what happens when you want to use a SwiftUI View from a UIView directly (i.e. a UIView deep down in the hierarchy wishes to use an existing SwiftUI view, and it doesn't have access to its UIViewController) – strangetimes Apr 22 '20 at 14:39
  • 2
    @strangetimes That isn't supported by SwiftUI. You should consider refactoring your code so your view's have access to their controllers. – Sam Spencer Jun 30 '20 at 18:33
  • 3
    @Imran I am trying to understand why would you need the UIViewController? `UIHostingController(rootView: ContentView()).view` you can "basically" convert the swiftUI view to UIView, everything seems to work, can you provide reason, why its "not supported"? –  Jun 21 '22 at 11:07