簡體   English   中英

UIViewControllerRepresentable 未正確占用空間

[英]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 的幫助。 這很有意義,為什么UIViewControllerRepresentableScrollView內會壓縮其框架。 我最終找到了解決問題的方法,其中涉及在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM