How can I navigate to Storyboard VC from SwiftUI View? - ios

I created this SwiftUI View:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
MasterView()
}.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
struct MasterView: View {
var body: some View {
Form {
Section(header: Text("Geplant")) {
Section {
NavigationLink(destination: /* How can I navigate to a Storyboard ViewController?*/) { Text("Berlin") }
}
}
}
.navigationBarTitle("Wähle Reise")
}
}
As you can read, I want to navigate at line "NavigationLink(destination: .....) to a Storyboard VC.
Before, I navigated to the SwiftUI ContentView using a UIHostingController from a UIKit VC.
Does anyone can help me?
Feel free to ask me for other Code/ Screenshots :)

Use UIViewControllerRepresentable
struct UIKitView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIKitViewController
func makeUIViewController(context: Context) -> UIKitViewController {
let sb = UIStoryboard(name: "Main", bundle: nil)
let viewController = sb.instantiateViewController(identifier: "UIKitViewController") as! UIKitViewController
return viewController
}
func updateUIViewController(_ uiViewController: UIKitViewController, context: Context) {
}
}
And then now use
NavigationLink(destination: UIKitView()) { Text("Berlin") }

Related

Ignore Bottom safe area in UIViewController presented from SwiftUI

I have a UIViewController that I am presenting from a SwiftUI view, previously when presenting from UIKit, I wasn't having this issue. The content at the bottom is being cut off for some reason, how can I fix this?
presented like this from a list:
.fullScreenCover(isPresented: $isPresented, onDismiss: nil, content: {
SingleTakeView(take: take)
})
struct SingleTakeView: UIViewControllerRepresentable {
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
var take: TakeOBJ
func makeUIViewController(context: Context) -> UIViewController {
let singleTakeVC = TakeSingleViewController(nibName: "TakeSingleView", bundle: nil, take: take)
let nav = UINavigationController(rootViewController: singleTakeVC)
nav.modalPresentationStyle = .fullScreen
return nav
}
}
You need to set the representable to ignore the safe area. You can either choose .vertical or .bottom.
.fullScreenCover(isPresented: $isPresented, onDismiss: nil, content: {
SingleTakeView(take: take).edgesIgnoringSafeArea(.vertical)
})

Pass data from UIViewControllerRepresentable to SwiftUI

I have a UIViewControllerRepresentable like this:
#ObservedObject var viewModel: HomeViewModel
typealias UIViewControllerType = ViewController
func makeUIViewController(context: Context) -> ViewController {
let storyboard = UIStoryboard(name: "ViewController", bundle: Bundle.main)
let viewController = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
// ...
return viewController
}
func updateUIViewController(_ uiViewController: UIKitInboxDetail, context: Context) {
// ...
}
Inside my viewController there is a collectionView.
Now I would like to detect if my collectionView is scrolled and if so perform some actions.
So i've added this method to my ViewController:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let collectionView = scrollView as? UICollectionView {
isFirstCollectionViewScrolled = true
}
}
But i can access this data only when the view is updated and not at run tiem.
Another approach i've tried is the Coordinator, but I cant perform what I want. Any advice?
Recap: I need to perform some action in my SwiftUI View when a scroll is detected.
Update:
class HomeViewModel: ObservableObject { }
And in my viewController i'm just implementing the usual CollectionView delegates.
If i print somethng in the metod ScrollViewDidEndDegelerating it works! I just neet do something in my swiftUI View when this happened! (A boolean maybe is not the best approach since it set it to true and then it stay true)

Hide UINavigationController's navigationBar when the root controller is a UIHostingController

I am struggling to hide the navigationBar, which would properly be hidden if the root controller wasn't a SwiftUI UIHostingController.
I tried the following:
Setting navigationController.isNavigationBarHidden = true after creating it, at viewDidLoad and viewWillAppear.
Adding both .navigationBarHidden(true) and .navigationBarBackButtonHidden(true) for the UIHostingController's rootView.
Could it be an Apple bug? I am using Xcode 11.6.
All my attempts together:
class LoginController: UINavigationController, ObservableObject
{
static var newAccount: LoginController
{
let controller = LoginController()
let view = LoginViewStep1()
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
controller.viewControllers = [UIHostingController(rootView: view)]
controller.isNavigationBarHidden = true
return controller
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.isNavigationBarHidden = true
}
override func viewDidLoad()
{
super.viewDidLoad()
self.isNavigationBarHidden = true
}
}
struct LoginViewStep1: View
{
// ...
var body: some View
{
VStack {
// ...
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
Here is a solution. Tested with Xcode 11.4 / iOS 13.4
Modified your code:
class LoginController: UINavigationController, ObservableObject
{
static var newAccount: LoginController
{
let controller = LoginController()
let view = LoginViewStep1()
controller.viewControllers = [UIHostingController(rootView: view)]
// make it delayed, so view hierarchy become constructed !!!
DispatchQueue.main.async {
controller.isNavigationBarHidden = true
}
return controller
}
}
struct LoginViewStep1: View
{
var body: some View
{
VStack {
Text("Hello World!")
}
}
}
tested part in SceneDelegate
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = LoginController.newAccount
self.window = window
window.makeKeyAndVisible()
}
Alternate solution is to use UINavigationControllerDelegate:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
// Required because pushing UIHostingController makes the navigationBar appear again
navigationController.isNavigationBarHidden = true
}
Tested with iOS 14.5 - Xcode 12.5

Passing Data from SwiftUI View to UIViewController

I've been testing SwiftUI to see how far I can go with it. Now, I want to find out whether or not I can pass a string from SwiftUI View directly to UIViewController. And I want to display this string with UILabel. UILabel is all I have on my storyoboard.
The following is my view controller (UIViewController).
import UIKit
import SwiftUI
class HomeViewController: UIViewController {
var message = String()
#IBOutlet weak var messageLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
messageLabel.text = message
}
}
struct HomeViewControllerRepresentation: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<HomeViewControllerRepresentation>) -> HomeViewController {
UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeViewController") as! HomeViewController
}
func updateUIViewController(_ uiViewController: HomeViewController, context: UIViewControllerRepresentableContext<HomeViewControllerRepresentation>) {
}
}
My SwiftUI View is the following.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: HomeViewControllerRepresentation(message: "GGG")) { // Error
Text("Tap me")
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
I hoped that I could just pass a string with HomeViewControllerRepresentation. But it won't work. Is there a simple way of passing data from SwiftUI View to UIViewController? Thanks.
UIViewControllerRepresentable is not a proxy, but wrapper, so everything should be transferred manually, like
struct HomeViewControllerRepresentation: UIViewControllerRepresentable {
var message: String
func makeUIViewController(context: UIViewControllerRepresentableContext<HomeViewControllerRepresentation>) -> HomeViewController {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeViewController") as! HomeViewController
controller.message = self.message
return controller
}
func updateUIViewController(_ uiViewController: HomeViewController, context: UIViewControllerRepresentableContext<HomeViewControllerRepresentation>) {
}
}

Views from storyboards won't appear in Xcode Preview

I try to use Xcode Preview feature. It works well when I add views directly in code, but if I add any views via storyboard, preview won't show these views. Here is my code:
import UIKit
final class ViewController: UIViewController {
#IBOutlet weak var segmentedControl: UISegmentedControl?
}
#if canImport(SwiftUI) && DEBUG
import SwiftUI
struct ViewControllerRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
return ViewController().view
}
func updateUIView(_ view: UIView, context: Context) {
}
}
#available(iOS 13.0, *)
struct ViewController_Preview: PreviewProvider {
static var previews: some View {
Group {
ViewControllerRepresentable()
}
}
}
#endif
That's my controller in simulator and storyboard:
That's how this controller looks in preview:
Set storyboardID (for example "ViewController") for your ViewController in storyboard.
Then create viewController from storyboard
func makeUIView(context: Context) -> UIView {
let viewController = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "ViewController")
return viewController.view
}
Use your storyboard name instead of "Main" if it's different.

Resources