How to present Storyboard ViewController SwiftUI? - swiftui

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

Related

UITapGestureRecognizer not triggered when used by 2 overlapping UIViewRepresentable

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
}

Remove title when pushing EKCalendarChooser to Navigation Stack with SwiftUI

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:

SwiftUI - How to make UIViewControllerRepresentable transparent?

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?

Multiple NavigationLinks leading to UIViewControllerRepresentable destination ends up with blank screen

I have found a minimal SwiftUI app that exhibits a bug:
class HelloWorldVC: UIViewController {
override func loadView() {
super.loadView()
let label = UILabel()
label.text = "Hello, World"
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
struct ViewControllerContainer: UIViewControllerRepresentable {
let vc: UIViewController
func makeUIViewController(context: Context) -> some UIViewController { vc }
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}
struct ContentView: View {
var body: some View {
NavigationView {
// Works:
//NavigationLink("View UIKit VC", destination: ViewControllerContainer(vc: HelloWorldVC()))
// Only loads the UIKit view once. It's blank on subsequent tries.
NavigationLink(
"Detail Screen",
destination: NavigationLink(
"View UIKit VC",
destination: ViewControllerContainer(vc: HelloWorldVC())
)
)
}
}
}
Steps to reproduce:
Tap "Detail Screen"
Tap "View UIKit VC". You will see the "Hello, World" UIViewController.
Tap Back
Tap "View UIKit VC"
Expected:
You should see the "Hello, World" UIViewController again
Actual:
You will see a blank view. This will happen as many times as you try.
Note: In the commented out code, it works properly if you only have one layer deep of NavigationLink.
You are not working with UIViewControllerRepresentable correctly. You need to create a new view controller inside makeUIViewController, reusing it breaks the view controller lifecycle in your case.
The UIViewControllerRepresentable properties can be passed to the view controller when you create or update it, as follows:
struct ViewControllerContainer: UIViewControllerRepresentable {
let props: Int
func makeUIViewController(context: Context) -> some UIViewController {
HelloWorldVC(props: props)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
uiViewController.props = props
}
}

Push UIViewController from SwiftUI with UINavigation with Back Button?

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
}
}