Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Button doesn't work in UIHostingController

Tags:

ios

uikit

swiftui

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?

like image 703
MattL Avatar asked Feb 11 '26 13:02

MattL


1 Answers

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
}
like image 65
MattL Avatar answered Feb 14 '26 01:02

MattL