How to get NSView from SwiftUI2 App entry - swift

I'm migrating my macOS app built with SwiftUI 1 to SwiftUI 2. There is a feature that allows user to save the main view as pdf using NSPrinterOperation, which takes an NSView instance.
Currently, the app is on SwiftUI 1 with AppDelegate, and I can get the main view through this code
Button("Print") {
let appDelegate = NSApp.delegate as! AppDelegate
let view = appDelegate.window.contentView! // returns the NSHostingView set up in AppDelegate.swift
...
NSPrintOperation(view: view, printInfo: newInfo).run()
}
Since upgrading to SwiftUI2, the AppDelegate is no longer there, and how can I get the view now?

See my comment for reference how to set app delegate (which you will be able then access), however also you can content view directly from window via NSApp, like
NSApp.mainWindow?.contentView
or
NSApp.keyWindow?.contentView
depending on your windows configuration.

Related

How to override Copy and Paste NSMenuItems for one View Controller Swift macOS

I am writing a macOS application with multiple view controllers, using Storyboards.
In my main View Controller I would like to be able to copy and paste data to the NSPasteboard. The data is related to buttons displayed to the user, and the exact data to be copied varies depending on which button has most recently been pressed/selected.
I would like to be able to override the standard behaviour of the Copy and Paste NSMenuItems when my main View Controller is the front most (key) window, but revert to back to standard behaviour when other windows are in the foreground, as they all contain NSTextFields which can be copied/pasted into.
I have done a lot of googling, and overriding this behaviour is not very well documented. I can achieve it globally by adding an IBAction into the App Delegate, which I could use to call a function in whichever View Controller is key, but this doesn't feel like a very elegant solution.
Currently my IBAction in the App Delegate looks like this:
#IBAction func copy(_ sender: Any) {
if let window = NSApplication.shared.keyWindow {
if let splitView = window.contentViewController as? SplitViewController {
if let controlVC = splitView.controlItem.viewController as? ControlViewController {
controlVC.copyAction(self)
}
}
}
}
Am I missing a neater solution?
Thanks,
Dan

UIAlertView is deprecated and unavailable for UIScene based applications, please use UIAlertController

I'm new to Swift and I'm getting this Error.
I don't get it because I don't use any type of alert in my code.
Here is my ViewController:
import UIKit
import WebKit
class ViewController: UIViewController{
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://www.google.com/maps")!
webView.load(URLRequest(url: url))
}
}
I only want to use WKWebView in my app and use the location.
I already added NSLocationAlwaysAndWhenInUseUsageDescription to the info.plist
You can reproduce this error if you using iOS 13, Xcode and this example and any website which uses the location like google maps.
I was also going through the same issue, i'm able to solve it getting help from a developer, scene base application that is new yet not support ui alert view so it crashes the app, we can remove the scene base application we can use window base app, follow these steps to resolve the issue:
First remove the SceneDelegate file
check this image for first step
Second remove UISceneSession Lifecycle two methods from AppDelegate file
check this image for second step
Third go to your info plist and remove Application Scene Manifest Entry from it
check this image for third step
Go ahead and build the project, run it now, make sure you have window property in AppDelegate

How to set initial view controller after button tapped

I have an app that has a begin journey activity and a choose category activity. I want to make it that after the user presses the start journey button he won't be able to return there or be able with a certain button.
Set the next viewController as the root viewController as described here: How to set the rootViewController with Swift, iOS 7
It seems like you’re looking for the root view of the app to be changed to the beginning of the journey, rather than the initial home screen. This can be done by setting the rootViewController property of the app’s main window, UIApplication.shared.keyWindow.
This can be done using the following code block:
let viewController = JourneyViewController()
guard
let window = UIApplication.shared.keyWindow
else {
return
}
window.rootViewController = viewController
Please note, I work in Xcode 11 beta with iOS 13. My apologies if this doesn’t work with older versions of iOS/OS X.

How to get back to the current window from AppDelegate

In my macOS application, I am following a OAuth-Login procedure.
I am authenticating successfully by receiving a code within a url through my custom url, with which I already can get hold of an access_token.
I start the login procedure with the simple click of a button in a ViewController.
The system I log in to then answers with my registered custom url scheme, providing me with a code I have to then use to get an access_token with POST request. This access_token than can be used to make api calls.
My Problem now is that lacking knowledge of how to implement other techniques, I am currently doing all the latter procedure within the AppDelegate function application(_ application: NSApplication, open urls: [URL]).
From my limited understanding this is the way to handle this, but now
how can I get back from there to get hold of the current view? I am really struggling with the view controller life cycle part of things here I guess...
In AppDelegate, you can get a reference to the current window's ViewController as follows. Replace "MainViewController" with the name of the one you use.
iOS Swift:
if let vc = window?.rootViewController as? MainViewController {
// use `vc` here by calling public functions
// and accessing public properties
}
macOS Swift:
if let vc = NSApplication.shared.mainWindow?.contentViewController as? MainViewController {
// use `vc` here by calling public functions
// and accessing public properties
}
OK, found it: since there is no rootViewController in macOS-land as there is with iOS, it works a little different:
in macOS you can get hold of the window currently "under keyboard" like so:
in application(_:open:), where the redirect_uri gets called:
if let viewController = NSApplication.shared.keyWindow?.contentViewController as? ViewController {
// make changes to the view
}

Trigger In-App-Browser after tapping on Annotation with PDFKit - iOS 11

I´m working on an app with Apple's PDFKit and have managed to put some Annotation-Buttons with a working PDFActionURL, which open the iOS standard browser after tapping on them.
Unfortunately, I did not find a working solution, how to open the associated links in an In-App-Browser or to load it in a webview. After tapping the AnnotationButton PDFKit automatically opens Safari and I haven´t found a property or another way concerning iOS to influence this behavior by manipulating f.e. the PDFAction.
let urlString = "https://www.apple.com"
let urlAction = PDFActionURL(url: urlString)
urlButton.action = urlAction
pdfPage.addAnnotation(urlButton)
Is there a way to force the call of an In-App-Browser at every UIApplication.shared.open() or to manipulate the execution of a PDFAction?
Finally, I figured it out for myself ;) I´ve overlooked one function of the PDFViewDelegate
By setting the delegate of the pdfView to the involved ViewController and using this function
func pdfViewWillClick(onLink sender: PDFView, with url: URL) {
print(url)
}
you are able to use the associated URL of the PDFActionButton and to trigger an In-App-Browser (e.g. SFViewController).
You want to instantiate the view like so:
let webV:UIWebView = UIWebView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
webV.loadRequest(NSURLRequest(URL: NSURL(string: "http://www.google.co.in")))
webV.delegate = self;
self.view.addSubview(webV)
Found HERE
To do it right, I would create a new ViewController in your storyboard, nested in a navigationController and segue to the view on annotation selection. Display your content on viewDidLoad using a similar set of code as seen above. That code is probably in an older version of swift so you'll need to update it. There is more detail about using the delegate associated in the link provided.