I have a UIViewControllerRepresentable which I'm presenting like this:
.fullScreenCover(item: $userToPresent) { user in
ProfileCardViewRepresentable(user: user)
.ignoresSafeArea()
}
The thing is, I want to have transparency on its background so that the view behind it is visible. If I present the ViewController from another ViewController and set the modalPresentationStyle to .overFullScreen, it works! But when I try to present it from a SwiftUI view, it just shows a grey background.
Here's my Representable:
struct ProfileCardViewRepresentable: UIViewControllerRepresentable {
let user: NPUserDataModel
func makeUIViewController(context: Context) -> ProfileCardViewController {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ProfileCardViewController") as! ProfileCardViewController
vc.user = user
vc.modalPresentationStyle = .overFullScreen
return vc
}
func updateUIViewController(_ uiViewController: ProfileCardViewController, context: Context) {}
}
Any idea what is wrong?
Related
Because I need to know whether user taps with finger or pencil, I had to create a UIViewRepresentable with a UITapGestureRecognizer. Everything works fine when this View is used with other SwiftUI views. But when stack 2 of this view, and I clip the top one, the other one never catches any tap event.
Here is my custom view:
struct UIKitView: UIViewRepresentable {
let color: UIColor
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = color
let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(gesture:)))
view.addGestureRecognizer(tapGesture)
return view
}
func updateUIView(_ uiView: UIView, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(color: color)
}
class Coordinator: NSObject {
let color: UIColor
init(color: UIColor) {
self.color = color
}
#objc func handleTap(gesture: UITapGestureRecognizer) {
print("Tapped in \(color == .yellow ? "yellow" : "red")")
}
}
}
And here how it is used in SwiftUI View:
ZStack {
UIKitView(color: .yellow)
UIKitView(color: .red)
.clipShape(Circle())) // not required, just visual
.contentShape(Circle()))
}
The tap is always catched by the red view.
Then I've overriden the hitTest() function of my UIVIewRepresentable to check if the tap is inside the clipîng shape. The tap is then detected in the red circle, but nothing tiggers anymore outside.
Any idea why?
You can override this method in CircleView:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let center = CGPoint(x: bounds.size.width/2, y: bounds.size.height/2)
return pow(center.x-point.x, 2) + pow(center.y - point.y, 2) <= pow(bounds.size.width/2, 2)
}
Source
FYI there is also a mistake in your UIViewRepresentable, you need this
func updateUIView(_ uiView: UIView, context: Context) {
view.backgroundColor = color
}
I'm working on an app where I want to push the EKCalendarChooser View Controller to the navigation stack with a navigation link. Everything works as expected apart from the fact that I can't get rid of some magic title/label.
I want to hide the title marked with the red rectangle in the image.
I'm using the following code to push the view:
NavigationLink(destination: CalendarChooser(eventStore: self.eventStore)
.edgesIgnoringSafeArea([.top,.bottom])
.navigationTitle("My Navigation Title")) {
Text("Calendar Selection")
}
And this is my UIViewControllerRepresentable
import SwiftUI
import EventKitUI
struct CalendarChooser: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
#Environment(\.presentationMode) var presentationMode
let eventStore: EKEventStore
func makeUIViewController(context: UIViewControllerRepresentableContext<CalendarChooser>) -> UINavigationController {
let chooser = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars, entityType: .event, eventStore: eventStore)
chooser.selectedCalendars = Set(eventStore.selectableCalendarsFromSettings)
chooser.delegate = context.coordinator
chooser.showsDoneButton = false
chooser.showsCancelButton = false
return UINavigationController(rootViewController: chooser)
}
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
}
class Coordinator: NSObject, UINavigationControllerDelegate, EKCalendarChooserDelegate {
var parent: CalendarChooser
init(_ parent: CalendarChooser) {
self.parent = parent
}
func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
let selectedCalendarIDs = calendarChooser.selectedCalendars.compactMap { $0.calendarIdentifier }
UserDefaults.savedCalendarIDs = selectedCalendarIDs
NotificationCenter.default.post(name: .calendarSelectionDidChange, object: nil)
parent.presentationMode.wrappedValue.dismiss()
}
func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
parent.presentationMode.wrappedValue.dismiss()
}
}
}
Note that I'm not even sure that I'm on the right track here and I'm open for any solution.
I think I've found a solution to my own problem. With a small modification
to my UIViewControllerRepresentable the view looks the way I want it to. More specifically to the updateUIViewController function:
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
uiViewController.setNavigationBarHidden(true, animated: false) // This line!
}
By doing this I keep the navigation controls and title from the navigation link, which looks like this:
I have a modal sheet in which half of the screen, pervious was shown to the user by present(), I want to see the same effect from swift UI
struct StripePaymentViewNav: UIViewControllerRepresentable {
typealias UIViewControllerType = StripePaymentWithPromoCodeViewController
func makeUIViewController(context: Context) -> StripePaymentWithPromoCodeViewController {
let storyboard = UIStoryboard(storyboard: .main)
let stripePaymentWithPromoCodeView = storyboard.instantiateViewController(withIdentifier: "StripePaymentWithPromoCodeView") as! StripePaymentWithPromoCodeViewController
stripePaymentWithPromoCodeView.modalPresentationStyle = .overFullScreen
return stripePaymentWithPromoCodeView
}
func updateUIViewController(_ uiViewController: StripePaymentWithPromoCodeViewController, context: Context) {
}
}
.sheet(isPresented: $viewModel.state.navigateToStripePayment, content: {
StripePaymentViewNav()
})
this worked however, half of the screen is the modal sheep and the other half is white
I am not getting the proper solution, How to open UIKit ViewController(With Navigation Controller) from SwiftUI Button clicked.
You can do it by using the UIViewControllerRepresentable protocol which is used to manage your UIKit ViewController in the SwiftUI.
Here is the code to show your ViewController from SwiftUI View interface.
SwiftUI View
struct ContentView: View {
#State var isOpenView = false
var body: some View {
NavigationView {
VStack {
//show your view controller
NavigationLink(destination: TestControllerView(), isActive: $isOpenView) {
EmptyView()
}
Button(action: {
self.isOpenView = true
}){
Text("Tap Me")
}
}
}
}
}
Now wrap your ViewController into UIViewControllerRepresentable
struct TestControllerView : UIViewControllerRepresentable {
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
func makeUIViewController(context: Context) -> some UIViewController {
guard let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "TestViewController") as? TestViewController else {
fatalError("ViewController not implemented in storyboard")
}
return viewController
}
}
Here is your ViewController.Swift File code
import UIKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnBackClicked(_ sender : UIButton) {
self.dismiss(animated: true, completion: nil)
}
}
First, wrap your UIViewController in a UIViewControllerRepresantable like so.
Then present that View using .fullScreenCover
I need to open UIViewController from SwiftUI with UINavigation having back button.
Currently, I had implemented this via ViewControllerRepresentable and NavigationLink, its works, but I have two Navigations SwiftUI(NavigationLink with back button) and default UINavigation without back button, and I would like to perform to have my default UINavigation with the back button and hide SwiftUI(NavigationLink) navigation.
struct UIKitVc : UIViewControllerRepresentable {
let navigationController = UINavigationController()
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
}
func makeUIViewController(context: Context) -> UINavigationController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "SearchResult") as! SearchResultViewController
self.navigationController.addChild(viewController)
return self.navigationController
}
}
NavigationLink(destination: UIKitVc(){}
Thank you in advance.
If you're going to use NavigationView/NavigationLink then you don't need to use UINavigationController, just use representable over your UIViewController, like
struct UIKitVc : UIViewControllerRepresentable {
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
func makeUIViewController(context: Context) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier:
"SearchResult") as! SearchResultViewController
// ... make additional set up here if needed
return viewController
}
}