iosswiftuiuikit

SwiftUI Button doesn't work in UIHostingController


I have a SwiftUI View that I am embedding in an existing UIViewController using UIHostingController. The SwiftUI view is simple, in fact I can reduce it down to this code and reproduce the issue:

let hostingController = UIHostingController(rootView: Button {
   print("tapped")
} label {
   Text("Tap")
}

The hostingController is added to my existing view controller as a child like this:

override func viewDidLoad() {
   super.viewDidLoad()
   view.addSubview(hostingController.view)
   // Code to set up autolayout constraints omitted.
   addChild(hostingController)
   hostingController.didMove(toParent: self)
}

The button is tappable in the canvas preview, but not in the simulator or on a real device. There are no gesture recognizers or other views covering the UIHostingController's view. I tried using .onTapGesture(perform:) instead of a Button but that didn't work either. To make things weirder, I can add a ScrollView as a subview of my SwiftUI and scrolling works. Why won't my button work?


Solution

  • Apparently the issue was that the parent UIViewController was using a layer transform to animate itself onto the screen. This transform totally broke all SwiftUI tap gestures. When I changed the layer transform code to just change the view's frame everything worked.

    The offending transform code looked like this:

    view.transform = CGAffineTransform(translationX: -300, y: 0)
    
    UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
        self.view.transform = CGAffineTransform.identity
    }
    

    And I changed it to something like this:

    view.frame.origin.x = view.frame.origin.x - 300
    
    UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
        self.view.frame.origin.x = self.view.frame.origin.x + 300
    }