我正在尝试在SwiftUI视图中使用自定义UIViewController。我设置了一个UIViewControllerRepresentable类,它在makeUIViewController方法中创建UIViewController。这将创建UIViewController并显示按钮,但是,UIViewControllerRepresentable不会占用任何空间。
我尝试使用UIImagePickerController而不是我的自定义控制器,并且大小正确。让我的控制器占用空间的唯一方法是在我的SwiftUI视图中的UIViewControllerRepresentable上设置一个固定的框架,这是我绝对不想做的。
注意:我确实需要使用UIViewController,因为我正在尝试在SwiftUI中实现UIMenuController。我得到了所有的工作,除了这个问题,我有与它不正确的大小。
下面是我的代码:
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
}
}我的SwiftUI视图:
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)
}
}
}截图:

我没有太多使用UIKit的经验--我唯一的经验是编写在SwiftUI中使用的UIKit视图。这个问题很可能与我缺乏UIKit知识有关。
提前感谢!
编辑:
以下是EntityViewItem的代码。我还将提供ClientView在EntityView中的容器视图。
我还清理了其余代码,并用硬编码的值替换了对Entity的引用。
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)
}
}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()
}
}
}
}发布于 2021-04-25 01:46:17
非常感谢@udbhateja和@jnpdx的帮助。这就解释了为什么UIViewControllerRepresentable在ScrollView中压缩它的框架。我最终找到了一个解决我的问题的方法,其中包括在UIViewControllerRepresentable上设置一个固定的高度。基本上,我使用PreferenceKey来查找SwiftUI视图的高度,并设置UIViewControllerRepresentable的框架以与之匹配。
如果有人遇到同样的问题,下面是我的代码:
struct EntityViewItem<Label: View, Content: View>: View {
var copyValue: String
var label: Label
var content: Content
var action: (() -> Void)?
@State var height: CGFloat = 0
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 {
ViewControllerRepresentable(copyValue: copyValue) {
SizingView(height: $height) { // This calculates the height of the SwiftUI view and sets the binding
VStack(alignment: .leading, spacing: 2) {
// Content
}
.padding([.leading, .trailing])
.padding(.top, 10)
.padding(.bottom, 10)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.frame(height: height) // Here I set the height to the value returned from the SizingView
}
}和SizingView的代码
struct SizingView<T: View>: View {
let view: T
@Binding var height: CGFloat
init(height: Binding<CGFloat>, @ViewBuilder view: () -> T) {
self.view = view()
self._height = height
}
var body: some View {
view.background(
GeometryReader { proxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: proxy.size)
}
)
.onPreferenceChange(SizePreferenceKey.self) { preferences in
height = preferences.height
}
}
func size(with view: T, geometry: GeometryProxy) -> T {
height = geometry.size.height
return view
}
}
struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}完成此操作后,我的UIMenuController就可以完全发挥作用了。它有很多代码(如果这个功能存在于SwiftUI中,我可能不得不写5行代码),但它工作得很好。如果任何人喜欢的代码,请评论,我会分享。
以下是最终产品的图像:

发布于 2021-04-24 15:52:25
正如@jnpdx提到的,你需要通过框架提供显式的大小,以使可表示项可见,因为它嵌套在VStack和其他视图中。
如果您有使用UIViewController的特殊原因,那么一定要提供显式框架,或者创建一个SwiftUI视图。
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)
.frame(height: 100.0)
EntityViewItem(copyValue: "copy value", label: {
Text("Route")
}, content: {
HStack(alignment: .center) {
Text("Random route name")
}
})
.border(Color.blue)
}
}
}https://stackoverflow.com/questions/67234508
复制相似问题