UIApplicationShortcutItem in Swift to support 3D touch - swift

How to launch and navigate to a particular view controller using short cut items?
I am using UINavigationController in my app. The issue is when I click on the short cut items, I have to navigate directly to last view controller. In the last view controller, I will have Back button to navigate back to previous screens. Is this possible?
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: Bool -> Void) {
let handledShortCutItem = handleShortCutItem(shortcutItem)
completionHandler(handledShortCutItem)
}
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
var handled = false
// Verify that the provided `shortcutItem`'s `type` is one handled by the application.
guard ShortcutIdentifier(fullType: shortcutItem.type) != nil else { return false }
guard let shortCutType = shortcutItem.type as String? else { return false }
switch (shortCutType) {
case ShortcutIdentifier.LastViewController.type:
print("Inside Agent **********")
**// Navigate to view controller**
handled = true
break
default:
break
}

Related

Populate TableView and CollectionView when user is Logged-in

My current program is about mostly TableViews and CollectionViews that fetches data from an API, The problem is that the data is fetched only when the user goes to the current tab which the TableView is present and the data fetches from the moment the user went to the tab and needs to wait till the data come because when user logs-in a token is shared with other classes and used later to fill them.
I don't know how can I make the data filled automatically after the user is logged-in not when the user goes to the specific tab and wait for them to load, Basically make a request when the user loggs-in so he doesn't have to go to the tabs and wait 5+ seconds
signInViewController:
#IBAction func signInSegueToDashboard(_ sender: Any) {
activityLoaderSignIn.startAnimating()
APICallerPOST.shared.signInToAccount(username: emailFieldSignIn.text!, password: passwordFieldSignIn.text!) { [self] (result, error) in
if result?.StatusCode != 0 {
DispatchQueue.main.async {
activityLoaderSignIn.stopAnimating()
}
}
switch result?.StatusCode {
case 0:
APICallerGET.shared.token = result?.Result.token
assignDataFromSignIn.DefaultsKeys.keyOne = (result?.Result.completeName)!
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController")
else {
return
}
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
case 1:
DispatchQueue.main.async {
}
case 2:
DispatchQueue.main.async {
}
default:
break
}
}
}
invoicesViewController:
// the function which is used at viewDidLoad at a TableView class
func fetchAndReloadData(){
APICallerGET.shared.propertiesOfAccount(for: APICallerGET.shared.token!) { [self] (result, error) in
switch result?.StatusCode {
case 0:
self.notificationsNew = result!
self.notifications = self.notificationsNew
DispatchQueue.main.async {
self.invoicesTableView.reloadData()
activityLoaderInvoices.stopAnimating()
}
case 1:
print("error")
default:
break
}
}
}
Now if there is a way to load this function of the invoicesViewController straight when the user signs-in so he doesn't have to wait till the data loads when he went to the tab with the TableView.

NSTouchBar integration not calling

I am integrating TouchBar support to my App. I used the how to from Rey Wenderlich and implemented everything as follows:
If self.touchBarArraygot filled the makeTouchBar() Method returns the NSTouchBar object. If I print out some tests the identifiers object is filled and works.
What not work is that the makeItemForIdentifier method not get triggered. So the items do not get created and the TouchBar is still empty.
Strange behavior: If I add print(touchBar) and a Breakpoint before returning the NSTouchBar object it works and the TouchBar get presented as it should (also the makeItemForIdentifier function gets triggered). Even if it disappears after some seconds... also strange.
#available(OSX 10.12.2, *)
extension ViewController: NSTouchBarDelegate {
override func makeTouchBar() -> NSTouchBar? {
if(self.touchBarArray.count != 0) {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = NSTouchBarCustomizationIdentifier("com.TaskControl.ViewController.WorkspaceBar")
var identifiers: [NSTouchBarItemIdentifier] = []
for (workspaceId, _) in self.touchBarArray {
identifiers.append(NSTouchBarItemIdentifier("com.TaskControl.ViewController.WorkspaceBar.\(workspaceId)"))
}
touchBar.defaultItemIdentifiers = identifiers
touchBar.customizationAllowedItemIdentifiers = identifiers
return touchBar
}
return nil
}
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
if(self.touchBarArray.count != 0) {
for (workspaceId, data) in self.touchBarArray {
if(identifier == NSTouchBarItemIdentifier("com.TaskControl.ViewController.WorkspaceBar.\(workspaceId)")) {
let saveItem = NSCustomTouchBarItem(identifier: identifier)
let button = NSButton(title: data["name"] as! String, target: self, action: #selector(self.touchBarPressed))
button.bezelColor = NSColor(red:0.35, green:0.61, blue:0.35, alpha:1.00)
saveItem.view = button
return saveItem
}
}
}
return nil
}
}
self.view.window?.makeFirstResponder(self) in viewDidLoad() did solve the problem.

Dynamic NSTooltips: Event Key Modifier(s) Affecting Tooltips

Is there a notification mechanism for tooltips, so then key modifiers can be used to make them dynamic? I do not know of one so went about this path to capture the tooltip view being created and trying to trigger a redraw when a key event occurs.
I monitor key modifier(s) (in my app delegate); focusing on SHIFT here but any key modifier will do:
var localKeyDownMonitor : Any? = nil
var globalKeyDownMonitor : Any? = nil
var shiftKeyDown : Bool = false {
didSet {
let notif = Notification(name: Notification.Name(rawValue: "shiftKeyDown"),
object: NSNumber(booleanLiteral: shiftKeyDown));
NotificationCenter.default.post(notif)
}
}
which the app's startup process will install ...
// Local/Global Monitor
_ /*accessEnabled*/ = AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary)
globalKeyDownMonitor = NSEvent.addGlobalMonitorForEvents(matching: NSEventMask.flagsChanged) { (event) -> Void in
_ = self.keyDownMonitor(event: event)
}
localKeyDownMonitor = NSEvent.addLocalMonitorForEvents(matching: NSEventMask.flagsChanged) { (event) -> NSEvent? in
return self.keyDownMonitor(event: event) ? nil : event
}
and then intercept to post notifications via app delegate didSet above (so when the value changes a notification is sent!):
func keyDownMonitor(event: NSEvent) -> Bool {
switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
case [.shift]:
self.shiftKeyDown = true
return true
default:
// Only clear when true
if shiftKeyDown { self.shiftKeyDown = false }
return false
}
}
For singleton tooltip bindings, this can be dynamic by having the notification inform (KVO of some key) I'd like a view delegate routine to act on this:
func tableView(_ tableView: NSTableView, toolTipFor cell: NSCell, rect: NSRectPointer, tableColumn: NSTableColumn?, row: Int, mouseLocation: NSPoint) -> String {
if tableView == playlistTableView
{
let play = (playlistArrayController.arrangedObjects as! [PlayList])[row]
if shiftKeyDown {
return String(format: "%ld play(s)", play.plays)
}
else
{
return String(format: "%ld item(s)", play.list.count)
}
}
...
So I'd like my notification handler to do something like
internal func shiftKeyDown(_ note: Notification) {
let keyPaths = ["cornerImage","cornerTooltip","<table-view>.<column-view>"]
for keyPath in (keyPaths)
{
self.willChangeValue(forKey: keyPath)
}
if subview.className == "NSCustomToolTipDrawView" {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "CustomToolTipView"), object: subview)
}
for keyPath in (keyPaths)
{
self.didChangeValue(forKey: keyPath)
}
}
where I would like to force the tooltip to redraw, but nothing happens.
What I did to obtain the tooltip view, was to watch any notification (use nil name to do this) that looked promising and found one - by monitoring all notifications and so I post to it for interested view controller which is the delegate of the tableView. The view controller is observing for the "CustomToolTipView" and remembers the object send - tooltipView; this is the new tooltip view created.
But nothing happens - shiftKeyDown(), to redraw the view.
I suspect it won't update in the current event loop but I do see the debug output.
With Willeke's suggestion I migrated a tableView to be cell based, added a get property for the tooltip and had the relevant object's init() routine register notifications for the shift key changes. This resulted in less code. A win-win :-)

UIWebView inside UIPageViewController crashes app

I have an app which needs a fix for the crash details below. Flow goes like this , user launches app, news articles are displayed on uitableview each cell has one article details. When user clicks on any cell we set all articles on uipageviewcontroller using pageViewController.setViewControllers([newsDetails!], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
If the articles are displayed only on text view, swiping controller to left and right works perfectly. But if there are 2 consecutive web view articles, swipe right (from web view article move to next web view article) and then left (back to web view article) the app crashes with this crash log.
Just to experiment I put my data into textview instead of uiwebview and it still crashed with same crash log, this articles use iframe tag in it. Code is here :
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
//----------------------------------------------------------------------
// Returning the article's detail view controller for the previous index
print("VIEW CONTROLLER AFTER")
return articleDetailViewController(for: detailPageIndex()-1,fromDetail:false)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
//------------------------------------------------------------------
// Returning the article's detail view controller for the next index
print("VIEW CONTROLLER BEFORE")
return articleDetailViewController(for: detailPageIndex()+1,fromDetail:false)
}
// We create a new instance of detail View Controller when ever a new VC is requested
func articleDetailViewController(for index: Int,fromDetail :Bool) -> MISNewsDetailViewController? {
let allNews:Bool
if(UserDefaults.standard.object(forKey: "IsAllNewsSelected") == nil || (UserDefaults.standard.object(forKey: "IsAllNewsSelected") as! String) == "YES"){
allNews = true
}else{
allNews = false
}
//--------------------------------------------
// If the index is out of bound, nil is return
if(allNews){
if (index == -1 || index >= (allArticles.count)) {
return nil
}
}else{
if (index == -1 || index >= (myArticles.count)) {
return nil
}
}
//---------------------------------------------------
// Getting the article object for the requested index
let article: MISArticle?
let detailViewController = storyboard?.instantiateViewController(withIdentifier: "MISNewsDetailViewController") as? MISNewsDetailViewController
let myAppDelegate = (UIApplication.shared.delegate as! AppDelegate)
if(myAppDelegate.isLaunchedFromWidget){
article = self.getTheWidgetSelectedArticleBaasedOnGlobalId();
}else{
if(allNews){
article = allArticles[index]
detailViewController?.isAllNewsPost = true
}else{
article = myArticles[index]
detailViewController?.isAllNewsPost = false
}
}
detailViewController?.mPost = article
detailViewController?.isRefreshPost = fromDetail
return detailViewController
}

Navigating to a ViewController from AppDelegate triggered by UIApplicationShortcutItem

In my application, the first view that is launched is controlled by RootUIViewController. Now the user can tap on any of the buttons on this view and then segue to a view controlled by LeafUIViewController. Each button points to this same view, with different value for some of the arguments.
Now I have implemented the 3D Touch shortcut menu which correctly calls the following function in the AppDelegate:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void)
In this function, I want to go to the LeafUIViewController such that the user can still navigate back to the RootViewController.
What is the right way to do this so that the Root is correctly instantiated, pushed on stack and then view navigates to the Leaf?
I suggest against doing any launch actions specific segues from that callback. I usually set a global state and handle it in all my relevant viewcontrollers. They usually pop to the main viewcontroller. In there I do programatically do the action just as would the user normally do.
The advantage is that the viewcontroller hierarchy is initialised in a normal way. The state can be then handled by the first viewcontroller that's going to be displayed on screen which isn't necessarily the first viewcontroller of the app. The application could be already initialised when the user triggers the action. So there could be a random viewcontroller in the hierarchy.
I use something like:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if #available(iOS 9.0, *) {
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
handleShortcutItem(shortcutItem)
}
}
return true
}
enum ShortcutType: String {
case myAction1 = "myAction1"
case myAction2 = "myAction2"
}
#available(iOS 9.0, *)
func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
switch shortcutType {
case .myAction1:
MyHelper.sharedInstance().actionMode = 1
return true
case .myAction2:
MyHelper.sharedInstance().actionMode = 2
return true
default:
return false
}
}
return false
}
and then in main viewController (such as main menu) handle the action somehow:
override func viewDidAppear() {
super.viewDidAppear()
switch MyHelper.sharedInstance().actionMode {
case 1:
// react on 1 somehow - such as segue to vc1
case 2:
// react on 2 somehow - such as segue to vc2
default:
break
}
// clear the mode
MyHelper.sharedInstance().actionMode = 0
}
And in other vc's:
override func viewDidLoad() {
super viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reloadView", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
func reloadView() {
if MyHelper.sharedInstance().actionMode {
self.navigationController.popToRootViewControllerAnimated(false)
}
}
This might not be the best if you are using several vc's. If there is something better, I'love to learn that :)