[英]UIViewControllerRepresentable not working correctly when wrapped in AnyView
[英]UIViewControllerRepresentable not correctly taking up space
我正在嘗試在 SwiftUI 視圖中使用自定義UIViewController
。 我設置了一個UIViewControllerRepresentable
class ,它在makeUIViewController
方法中創建了UIViewController
。 這將創建UIViewController
並顯示按鈕,但是UIViewControllerRepresentable
不占用任何空間。
我嘗試使用UIImagePickerController
而不是我的自定義 controller,並且大小正確。 我讓我的 controller 占用空間的唯一方法是在我的 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 沒有太多經驗 - 我唯一的經驗是編寫 UIKit 視圖以在 SwiftUI 中使用。 這個問題很可能與我缺乏 UIKit 知識有關。
提前致謝!
這是EntityViewItem
的代碼。 我還將提供ClientView
所在的容器視圖 - EntityView
。
我還清理了代碼的 rest 並將對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()
}
}
}
}
非常感謝@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 行代碼),但效果很好。 如果有人想要代碼,請發表評論,我會分享。
正如@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)
}
}
}
如果其他人試圖找到一個更簡單的解決方案,那么可以查看 controller 並調整大小以適應其內容:
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) {
}
}
然后,當您在 SwiftUI 中使用它時,請確保調用.fixedSize()
:
struct MainView: View {
var body: some View {
VStack(spacing: 0) {
ViewControllerContainer(MenuViewController())
.fixedSize()
}
}
}
對於任何尋找最簡單解決方案的人來說,@Edudjr 的回答中有幾行:
let size = content.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
content.preferredContentSize = size
只需將其添加到您的 makeUIViewController 中!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.