SwiftUI go back to RootView in sceneWillEnterForeground SceneDelegate.swift - swiftui

we try to reset the current View to Root if App will enter Foreground.
How can we do that in SwiftUI?
func sceneWillEnterForeground(_ scene: UIScene) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = RootView().environment(\.managedObjectContext, context)
if let windowScene = scene as? UIWindowScene {
let singleOrder = SingleOrder()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(singleOrder))
self.window = window
window.makeKeyAndVisible()
}
}

There is no "go back" but the possible approach is to recreate root view controller, by moving "by default" generated content creation into other delegate method as below...
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
self.window = window
}
}
func sceneWillEnterForeground(_ scene: UIScene) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = RootView().environment(\.managedObjectContext, context)
let singleOrder = SingleOrder()
window?.rootViewController = UIHostingController(rootView: contentView.environmentObject(singleOrder))
window?.makeKeyAndVisible()
}

Related

Swift: Different title placement depending on configuration

I have two configurations that are giving me different results:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let rootVC = MainController()
window.rootViewController = rootVC
self.window = window
window.makeKeyAndVisible()
}
class MainController: UIViewController {
var titleLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
titleLabel.text = "Grocery List"
titleLabel.textColor = .black
titleLabel.font = .systemFont(ofSize: 20, weight: .bold)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(titleLabel)
[enter image description here][1]
setupConstraints()
}
func setupConstraints() {
let padding: CGFloat = 15
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: padding),
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
}
First config
However, if I do
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
window?.rootViewController = UINavigationController(rootViewController: MainController())
}
It's different. Why? I think the main problem is I don't really know what the configuration details are doing, so I'm confused on why the different set ups give me different results. Thank you!
Second config

Start SwiftUI implementation of UIScrollView at specific scroll and zoom

I have some experience in SwiftUI, but am new to UIKit.
I'd like to import the zoom and position from one instance of an UIViewRepresentable UIKit ScrollView to another. So, basically, the user scrolls and zooms and later, in another branch of the view hierarchy, I want to start zoomed in at that zoom and position. I can't get it to work though, even after many attempts.
Below is my makeUIView function where I try to set the position and zoom that I want (after some initial setup).
func makeUIView(context: Context) -> UIScrollView {
// set up the UIScrollView
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.bouncesZoom = true
scrollView.delaysContentTouches = false
scrollView.maximumZoomScale = 0.85 * screenScale * 10
scrollView.minimumZoomScale = 0.85 * screenScale
// create a UIHostingController to hold our SwiftUI content
let hostedView = context.coordinator.hostingController.view!
hostedView.translatesAutoresizingMaskIntoConstraints = true
hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostedView.frame = scrollView.bounds
scrollView.addSubview(hostedView)
/*
Here I add the zoom and position
*/
scrollView.zoomScale = 0.85 * screenscale
// add zoom and content offset
if let zoomScale = zoomScale, let contentOffset = contentOffset {
scrollView.contentOffset = contentOffset
// make sure it is within the bounds
var newZoomScale = zoomScale
if zoomScale < scrollView.minimumZoomScale {
print("too small")
newZoomScale = scrollView.minimumZoomScale
} else if zoomScale > scrollView.maximumZoomScale {
print("too large")
newZoomScale = scrollView.maximumZoomScale
}
scrollView.setContentOffset(contentOffset, animated: true)
scrollView.setZoomScale(newZoomScale, animated: true)
}
return scrollView
}
The way I get the zoom and contentOffset in the first place is to grab the values from the Coordinator in first ScrollView instance using the below code. As far as I can tell this works well and I get updates with sensible values after zooming or scrolling. The first code snippet contains the makeCoordinator function where I initiate the coordinator with methods from an environmentObject (which then updates said object). The second snippet contains the Coordinator.
func makeCoordinator() -> Coordinator {
return Coordinator(hostingController: UIHostingController(rootView: self.content),
userScrolledAction: drawingModel.userScrollAction,
userZoomedAction: drawingModel.userZoomAction)
}
class Coordinator: NSObject, UIScrollViewDelegate {
var hostingController: UIHostingController<Content>
let userScrolledAction: (CGPoint) -> Void
let userZoomedAction: (CGFloat) -> Void
init(hostingController: UIHostingController<Content>, userScrolledAction: #escaping (CGPoint) -> Void, userZoomedAction: #escaping (CGFloat) -> Void) {
self.hostingController = hostingController
self.userScrolledAction = userScrolledAction
self.userZoomedAction = userZoomedAction
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return hostingController.view
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
userScrolledAction(scrollView.contentOffset)
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
userZoomedAction(scrollView.zoomScale)
}
}

Problem: How to call a SwiftUI View Struct Method from SceneDelegate

Currently I'm the one not getting it.
Problem:
Trying to connect to a View Method from a Scene Delegate e.g.:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
...
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
// guard let contentView = ???? as? ContentView else { return }
// contentView.callMethod(parameter: true)
}
}
struct ContentView: View {
var body: some View {
...
}
func callMethode(parameter: Bool) {
print("called")
}
}
Any clue how to connect to the View and call a method in-between?
thx
Jo
This is counter to the design of the SwiftUI framework. You should not have any persistent view around to call methods on. Instead, views are created and displayed as needed in response to your app's state changing.
For example, if your SceneDelegate had a reference to an instance of a model class, and your view depended on that model, you could modify the model in scene(_:continue:) and your view would update automatically.
If you can provide more specifics on what you're attempting to do, I may be able to give a more specific answer.
Many thanks. Fully understood... Will read the docs...
So is this a possible way as an example:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let activity = UserActivityManager()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(activity))
self.window = window
window.makeKeyAndVisible()
if let userActvity = connectionOptions.userActivities.first {
guard let title = userActvity.title else { return }
activity.doAction(action: title)
}
}
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let title = userActivity.title else { return }
activity.doAction(action: title)
}
}

How can I launch a SwiftUI View without Navigation back tracking?

I want to launch a View as a standalone View without the navigation hierarchy. The reason that I don't want to use a NavigationButton is that I don't want the user to return to the calling form.
I have tried the following approach that is similar to how the first view is launched in ScenceDelegate but nothing happens:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let window = appDelegate.getNewWindow()
window.rootViewController = UIHostingController(rootView: NewView())
window.makeKeyAndVisible()
I have a legitimate reason not to use the navigation UI, I'm leaving the explanation out to keep this short. I'm avoiding Storyboards to keep this as a simple as possible.
Thank you for any solution suggestions.
This is how I accomplished loading a new root View.
I added the following to the AppDelegate code
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
...
func loadNewRootSwiftUIView(rootViewController: UIViewController)
{
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = rootViewController
self.window = window
window.makeKeyAndVisible()
}
}
And placed the following in my form:
struct LaunchView : View {
var body: some View {
VStack {
Button(
action: {
LaunchLoginView()
},
label: {
Text("Login")
}
)
}
}
}
func LaunchLoginView(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vContoller = UIHostingController(rootView: LoginView())
appDelegate.loadNewRootSwiftUIView(rootViewController: vContoller)
}

Swift 3 NavigationController segue back causes wkwebview to move into wrong position

I have a ViewController containing a WKWebView, the view is positioned correctly the first time it loads, but after moving to another view (I'm opening another view by intercepting links from the WebView) and pressing the navigation item (back button) it briefly appears in the right place, then reloads with the top of the webview behind the navigation bar so the top of the page is cut off.
class HomeVC: BaseViewController, WKNavigationDelegate, WKUIDelegate {
var webView: WKWebView?
override func viewDidAppear(_ animated: Bool) {
self.edgesForExtendedLayout = UIRectEdge.top;
super.viewDidLoad()
addSlideMenuButton()
let screenSize: CGRect = UIScreen.main.bounds
let frameRect: CGRect = CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height)
let url: NSURL = Bundle.main.url(forResource: "services", withExtension: "html")! as NSURL
let requestObj: NSURLRequest = NSURLRequest(url: url as URL);
self.webView = WKWebView(frame: frameRect)
self.webView?.load(requestObj as URLRequest)
self.webView?.navigationDelegate = self
self.webView?.uiDelegate = self
self.view = self.webView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationItem.title = ""
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.title = "SELECT A SERVICE"
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
let link: String = (webView.url?.absoluteString)!
print(link)
if(link == "file:///haircut") {
print(link)
self.openViewControllerBasedOnIdentifier("WebVC")
}
}
I've searched around and can't find any similar issues, nor can I see anything obvious in the code.
You have are calling super.viewDidLoad from func viewDidAppear(), what can cause unexpected behaviour. Therefore your UIViewController subclass will never notify its superclass, that the view has been loaded.
override func viewDidAppear(_ animated: Bool) {
// Do not do this before calling super!
self.edgesForExtendedLayout = UIRectEdge.top;
// You are calling the wrong the function for super
// It should be super.viewDidAppear(animated)
super.viewDidLoad()
addSlideMenuButton()
let screenSize: CGRect = UIScreen.main.bounds
let frameRect: CGRect = CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height)
let url: NSURL = Bundle.main.url(forResource: "services", withExtension: "html")! as NSURL
let requestObj: NSURLRequest = NSURLRequest(url: url as URL);
self.webView = WKWebView(frame: frameRect)
self.webView?.load(requestObj as URLRequest)
self.webView?.navigationDelegate = self
self.webView?.uiDelegate = self
self.view = self.webView
}