I am trying to accessing the Storyboard View Controller in SwiftUI by using UIViewControllerRepresentable. I want to hide the UIKit Tabbar which we applied on ItineraryViewController by using the planDetailViewController.hidesBottomBarWhenPushed = true But that solution not working.
let eventGroup : EventGroup?
func makeUIViewController(context: Context) -> UIViewController {
guard let planDetailViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ItineraryViewController") as? ItineraryViewController else {
fatalError("ViewController not implemented in storyboard")
}
planDetailViewController.userActionMode = .viewOnly
planDetailViewController.itinerary = eventGroup!.event
planDetailViewController.shouldDisableCalendarVC = true
planDetailViewController.hidesBottomBarWhenPushed = true
return planDetailViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}```
Your UIViewControllerRepresentable won't get pushed into a UINavigationController like in UIKit so hidesBottomBarWhenPushed will not be respected.
The best way to do this is using Introspect like in this answer: https://stackoverflow.com/a/64182729/3393964
Related
I use coordinator pattern in my app, but I have problem with instantiate view controllers. The problem is that I use different module for each tab bar controller.
So far I've used this approach
protocol Storyboarded {
static func instantiate() -> Self
}
extension Storyboarded where Self: UIViewController {
static func instantiate() -> Self {
let id = String(describing: self)
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
return storyboard.instantiateViewController(identifier: id) as! Self
}
}
And during creation tab bar coordinator:
class MainTabBarController: UITabBarController, Storyboarded {
let main = MainCoordinator(navigationController: UINavigationController())
let calendar = CalendarCoordinator(navigationController: UINavigationController())
let chart = ChartCoordinator(navigationController: UINavigationController())
let profile = ProfileCoordinator(navigationController: UINavigationController())
override func viewDidLoad() {
super.viewDidLoad()
main.start()
calendar.start()
chart.start()
profile.start()
viewControllers = [main.navigationController, calendar.navigationController, chart.navigationController, profile.navigationController]
}
My all view controllers conform to Storyboarded Protocol:
class HomeTableViewController: UIViewController, Storyboarded {}
And coordinator for each tab bar controller looks like this
class MainCoordinator: Coordinator {
var childCoordinators = [Coordinator]()
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = HomeTableViewController.instantiate()
vc.tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "home"), tag: 0)
navigationController.pushViewController(vc, animated: false)
}
}
The problem is that other tab bar controllers belong to others storyboard, not only "Main". Using instantiate() from protocol causes error. I wonder how to create protocol extension where I can initialise ViewControllers with different storyboard names, not only "Main".
Try add this:
protocol Storyboarded {
static var storyboardName: String { get }
static func instantiate() -> Self
}
extension Storyboarded where Self: UIViewController {
static var storyboardName: String {
"Main" // Default implementation
}
static func instantiate() -> Self {
let id = String(describing: self)
let storyboard = UIStoryboard(name: Self.storyboardName, bundle: Bundle.main)
return storyboard.instantiateViewController(identifier: id) as! Self
}
}
Now you can do:
extension HomeTableViewController: Storyboarded {
class var storyboardName: String {
"Home" // Other storyboard name here, overrides default implementation
}
}
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)
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>) {
}
}
I am working on my SwiftUI Project and every View is now in SwiftUI, however due to some limitations of SwiftUI I have to add Storyboard's ViewControllers into my SwiftUI project. I am trying this method,
struct AssetsListView: UIViewControllerRepresentable {
var taskID : String
public typealias UIViewControllerType = AssetsListViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<AssetsListView>) -> AssetsListViewController {
let assetsListVC = AssetsListViewController()
assetsListVC.taskID = taskID
return assetsListVC
}
func updateUIViewController(_ uiViewController: AssetsListViewController, context: UIViewControllerRepresentableContext<AssetsListView>) {
}
}
This works fine and even viewDidLoad() method of my Storyboard's ViewController calls, but I am unable to see any element on my Storyboard Screen. How can I render those elements? Just like the normal Storyboard stuff.
You just created controller by class initialiser, to instantiate it from storyboard you have to do like the following
func makeUIViewController(context:
UIViewControllerRepresentableContext<AssetsListView>) -> AssetsListViewController {
let storyboard = UIStoryboard(name: "Main", // < your storyboard name here
bundle: nil)
let assetsListVC = storyboard.instantiateViewController(identifier:
"AssetsListViewController") // < your controller storyboard id here
assetsListVC.taskID = taskID
return assetsListVC
}
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.