I am trying to use a custom UIViewController in a SwiftUI view. I set up a UIViewControllerRepresentable class which creates the UIViewController in the makeUIViewController method. This creates the UIViewController and displays the button, however, the UIViewControllerRepresentable does not take up any space.
I tried using a UIImagePickerController instead of my custom controller, and that sizes correctly. The only way I got my controller to take up space was by setting a fixed frame on the UIViewControllerRepresentable in my SwiftUI view, which I absolutely don't want to do.
Note: I do need to use a UIViewController because I am trying to implement a UIMenuController in SwiftUI. I got all of it to work besides this problem I am having with it not sizing correctly.
struct ViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MenuViewController {
let controller = MenuViewController()
return controller
}
func updateUIViewController(_ uiViewController: MenuViewController, context: Context) {
}
}
class MenuViewController: UIViewController {
override func viewDidLoad() {
let button = UIButton()
button.setTitle("Test button", for: .normal)
button.setTitleColor(.red, for: .normal)
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
button.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}
}
My SwiftUI view:
struct ClientView: View {
var body: some View {
VStack(spacing: 0) {
EntityViewItem(copyValue: "copy value", label: {
Text("Name")
}, content: {
Text("Random name")
})
.border(Color.green)
ViewControllerRepresentable()
.border(Color.red)
EntityViewItem(copyValue: "copy value", label: {
Text("Route")
}, content: {
HStack(alignment: .center) {
Text("Random route name")
}
})
.border(Color.blue)
}
}
}

I do not have much experience with UIKit - my only experience is writing UIKit views to use in SwiftUI. The problem could very possibly be related to my lack of UIKit knowledge.
Thanks in advance!
Here is the code for EntityViewItem. I will also provide the container view that ClientView is in - EntityView.
I also cleaned up the rest of the code and replaced references to Entity with hardcoded values.
struct EntityViewItem<Label: View, Content: View>: View {
var copyValue: String
var label: Label
var content: Content
var action: (() -> Void)?
init(copyValue: String, @ViewBuilder label: () -> Label, @ViewBuilder content: () -> Content, action: (() -> Void)? = nil) {
self.copyValue = copyValue
self.label = label()
self.content = content()
self.action = action
}
var body: some View {
VStack(alignment: .leading, spacing: 2) {
label
.opacity(0.6)
content
.onTapGesture {
guard let unwrappedAction = action else {
return
}
unwrappedAction()
}
.contextMenu {
Button(action: {
UIPasteboard.general.string = copyValue
}) {
Text("Copy to clipboard")
Image(systemName: "doc.on.doc")
}
}
}
.padding([.top, .leading, .trailing])
.frame(maxWidth: .infinity, alignment: .leading)
}
}
The container of ClientView:
struct EntityView: View {
let headerHeight: CGFloat = 56
var body: some View {
ZStack {
ScrollView(showsIndicators: false) {
VStack(spacing: 0) {
Color.clear.frame(
height: headerHeight
)
ClientView()
}
}
VStack(spacing: 0) {
HStack {
Button(action: {
}, label: {
Text("Back")
})
Spacer()
Text("An entity name")
.lineLimit(1)
.minimumScaleFactor(0.5)
Spacer()
Color.clear
.frame(width: 24, height: 0)
}
.frame(height: headerHeight)
.padding(.leading)
.padding(.trailing)
.background(
Color.white
.ignoresSafeArea()
.opacity(0.95)
)
Spacer()
}
}
}
}
If anyone else is trying to find an easier solution, that takes any view controller and resizes to fit its content:
struct ViewControllerContainer: UIViewControllerRepresentable {
let content: UIViewController
init(_ content: UIViewController) {
self.content = content
}
func makeUIViewController(context: Context) -> UIViewController {
let size = content.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
content.preferredContentSize = size
return content
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
And then, when you use it in SwiftUI, make sure to call .fixedSize():
struct MainView: View {
var body: some View {
VStack(spacing: 0) {
ViewControllerContainer(MenuViewController())
.fixedSize()
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With