I have a custom SwiftUI containerView which contains a set of SwiftUI buttons.
The SwiftUI button contains a UIKit UIView which I need to be used by the main UIViewConroller. The code looks something like as below:
public struct ContainerView: View {
public var body: some View {
LazyVStack {
ForEach(viewModel.buttonViewModels, id:\.self) { buttonViewModel in
CustomButton(buttonViewModel)
}
}
}
}
public class CustomButton: View {
public var backgroundUIView = BackgroundUIView()
public var body: some View {
Button(action: viewModel.tapEvent), label: {
ZStack {
backgroundUIView
Text(viewModel.title)
}
}
}
public BackgroundUIView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .blue
return view
}
public CustomHostingController: UIHostingController<ContainerView> {
public var containerViewModel: ContainerViewModel
public init?(containerViewModel: ContainerViewModel) {
self.containerVieModel = containerViewModel
super.init(rootView: ContainerView())
}
}
In CustomHostingController how can I access CustomButton at particular index I want something like this.
extension CustomHostingController {
func getButtonAtIndex(index: Int) -> CustomButton {
rootView.body.subviews[index]
}
}
I need to find the CustomButton instance at particular index in CustomHostingController.
Tried to find a way to find Swiftui button at particular index in ContainerView. like
extension CustomHostingController {
func getButtonAtIndex(index: Int) -> CustomButton {
rootView.body.*subviews[index]*
}
}
In unlike UIKit in SwiftUI there is no way to do this subViews[at index]
. How can I access CustomButton instance at particular index?
I need access to CustomButton because CustomHostingController is a childViewController to a UIKit UIViewController, and I want to present a third party provided Popover controller (UIKit implementation) which requires the UIView as the source view for presenting it. The third party popover controller initializer needs a UIView or UIButton as the parameter sourceView so that the arrow pointer of the Popover controller can point to the UIView or UIButton.
So I need to use the customButton instance like as below:
'[[ThirdPartyPopoverController alloc] initWithDescription: @"Test coachmark presented" fromSourceView:customButton.backgroundUIView];`
You'll need to expose the UIView (in this case, the backgroundUIView) to your CustomHostingController. This can be done by passing a callback to the CustomButton, which lets the hosting controller know when a new UIView is created.
try this:
public struct BackgroundUIView: UIViewRepresentable {
var onUIViewCreated: ((UIView) -> Void)?
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .blue
onUIViewCreated?(view)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
public struct CustomButton: View {
public var viewModel: ButtonViewModel
public var index: Int
public var onUIViewCreated: ((Int, UIView) -> Void)?
public var body: some View {
Button(action: viewModel.tapEvent) {
ZStack {
BackgroundUIView(onUIViewCreated: { view in
onUIViewCreated?(index, view)
})
Text(viewModel.title)
}
}
}
}
public struct ContainerView: View {
@ObservedObject var viewModel: ContainerViewModel
var onUIViewCreated: ((Int, UIView) -> Void)?
public var body: some View {
LazyVStack {
ForEach(viewModel.buttonViewModels.indices, id: \.self) { index in
CustomButton(viewModel: viewModel.buttonViewModels[index],
index: index,
onUIViewCreated: onUIViewCreated)
}
}
}
}
public class CustomHostingController: UIHostingController<ContainerView> {
public var containerViewModel: ContainerViewModel
private var buttonUIViews: [Int: UIView] = [:]
public init(containerViewModel: ContainerViewModel) {
self.containerViewModel = containerViewModel
super.init(rootView: ContainerView(viewModel: containerViewModel, onUIViewCreated: { [weak self] index, uiView in
self?.buttonUIViews[index] = uiView
}))
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func getButtonAtIndex(index: Int) -> UIView? {
return buttonUIViews[index]
}
}
and use like below:
let hostingController = CustomHostingController(containerViewModel: yourViewModel)
if let buttonView = hostingController.getButtonAtIndex(index: 2) {
let popover = ThirdPartyPopoverController(description: "Test", fromSourceView: buttonView)
// Present your popover
}