Reputation: 370
I am building an SwiftUI app with the SwiftUI app protocol.
I need to access some UIKIt functions, particularly the ones that control NavigationView as in here: https://stackoverflow.com/a/56555928/13727105.
Apparently, to do that I need to bind my SwiftUI view with a ViewController.
I've tried doing the following:
ContentView.swift
struct ContentView: View {
var body: some View {
ZStack {
ContentViewIntegratedController() // <- here
NavigationView{
ScrollView {
Text("Content View")
.navigationTitle("Content View")
}
}
}
}
}
class ContentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
struct ContentViewIntegratedController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
return ContentViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType,
context: UIViewControllerRepresentableContext<ContentViewIntegratedController>) {}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
but calling ContentViewIntegratedContoller
(on line 5) seems to create a new preview instead of modifying the existing one.
Is there anyway to integrate a ViewController to modify a SwiftUI view?
What I don't want to do:
ContentViewIntegratedViewController
.Is there any alternative ways I could access those functions without adding a ViewController to my SwiftUI view?
TIA.
Upvotes: 2
Views: 5559
Reputation: 882
struct ContentView: View {
var body: some View {
ContentViewIntegratedController(view: YourView())
.ignoresSafeArea()
}
}
--------------------------------------------------------------
struct YourView: View {
var body: some View {
ScrollView{
Text("Hello, World!")
}
}}
------------------------------------------------------
import Foundation
import SwiftUI
struct ContentViewIntegratedController :UIViewControllerRepresentable{
var view: YourView
init(view:YourView) {
self.view = view
}
func makeUIViewController(context: Context) -> UINavigationController{
let childView = UIHostingController(rootView: view)
let controller = UINavigationController(rootViewController:childView)
let appearance = UINavigationBarAppearance()
let searchController = UISearchController()
searchController.searchBar.barStyle = .black
appearance.backgroundColor = UIColor(Color(.red))
appearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
controller.navigationBar.topItem?.compactAppearance = appearance
controller.navigationBar.topItem?.scrollEdgeAppearance = appearance
controller.navigationBar.topItem?.standardAppearance = appearance
controller.navigationBar.topItem?.title = "navigation bar"
controller.navigationBar.prefersLargeTitles = true
searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Rechercher...", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])
searchController.searchBar.setValue("Annuler", forKey: "cancelButtonText")
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.searchTextField.leftView?.tintColor = .white
let sfConfiguration = UIImage.SymbolConfiguration(pointSize: 30)
let barCodeIcon = UIImage(systemName: "barcode.viewfinder")?.withTintColor(.white, renderingMode: .alwaysOriginal).withConfiguration(sfConfiguration)
searchController.searchBar.setImage(barCodeIcon, for: .bookmark, state:.normal)
searchController.obscuresBackgroundDuringPresentation = false
let attributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes(attributes, for: .normal)
controller.navigationBar.topItem?.hidesSearchBarWhenScrolling = false
controller.navigationBar.topItem?.searchController = searchController
return controller
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
}}
Upvotes: 3
Reputation: 30391
There is no way in pure SwiftUI to directly detect the changing between large
and inline
display modes. However, using SwiftUI-Introspect, you can drop down into UIKit to solve this.
I solved this by getting the view for the large title, and detecting when the alpha
property changed. This changes at the exact moment when the title changes from large
to inline
, or vice-versa.
Code:
struct ContentView: View {
@State private var observer: NSKeyValueObservation?
@State private var title: String = "Large Title"
var body: some View {
NavigationView {
ScrollView {
Text("Hello world!")
}
.navigationTitle(title)
}
.introspectNavigationController { nav in
let largeTitleView = nav.navigationBar.subviews.first { view in
String(describing: type(of: view)) == "_UINavigationBarLargeTitleView"
}
observer = largeTitleView?.observe(\.alpha) { view, change in
let isLarge = view.alpha == 1
title = isLarge ? "Large Title" : "Inline title"
}
}
}
}
Result:
Upvotes: 2