Open specific UITabBarController / ViewController from SceneDelegate.swift - swift

I have a Today Extension which has a UITableView.
When the user taps one of the items in the UITableView, it opens the containing app with a URL Scheme and passes some data so that the containing app can open a specific view for the item the user tapped.
The problem is, from SceneDelegate.swift I can handle the URL Scheme but it seems I can't make a specific UITabBarController show and open the detail view for the item tapped by the user.
Please note that I'm not trying to create them; they're already created from the storyboard and working. I just want it to 'Navigate' automatically as if the user had tapped the tableview item.
The below code is what I've tried:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
// This part, I'm handling URL Scheme which is working fine
let url = URLContexts.first!.url
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)
let items = urlComponents?.queryItems
var deepLinkArray: [URLQueryItem] = []
deepLinkArray = items!
// From here I will use datas I got from URL
if let statusValue = deepLinkArray[0].value, let indexPathSection = deepLinkArray[1].value, let indexPathRow = deepLinkArray[2].value {
todoStatusValueURL = Int(statusValue)!
indexPathSectionURL = Int(indexPathSection)!
indexPathRowURL = Int(indexPathRow)!
// Here I want to show one of UITabBarController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let snoozedVC = storyboard.instantiateViewController(withIdentifier: "SnoozedViewController") as! SnoozedViewController
snoozedVC.self.tabBarController?.selectedIndex = todoStatusValueURL! // And this is not showing what I want
// Additionally I want to show ItemDetailViewController as if user tapped the item in the tableview, and below code is also not working
let detailVC = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC
snoozedVC.navigationController?.pushViewController(detailVC, animated: true)
}

First check if snoozedVC and TabBarController for that specific snoozedVC exist.. it will give you idea whats wrong with your code ... then move forward according to its result to check which part of your code is not working
if let snoozedVC = storyboard.instantiateViewController(withIdentifier: "SnoozedViewController") as? SnoozedViewController {
if let tabBarController = snoozedVC.tabBarController {
tabBarController.selectedIndex = todoStatusValueURL!
} else {
print("tab controller not found")
}
} else {
print("SnoozedViewController not found")
}
}

Related

Making a new view controller appear after interstitial ad is dismissed

I am building an iOS app in swift with Xcode that has a button that, when pressed, will (among other things) show an interstitial Admob ad, and then go to a different view controller. Right now, when I press the button, an interstitial ad will appear. However, when I close the ad, the view is still on the original page. If I click the button again, then I am taken to the second view controller. Sometimes, however, I have to dismiss three or four ads before I get to the second view controller. How can I make it so that, after I dismiss the interstitial ad, I go straight to the second view controller?
Here is the current code:
#IBAction func postPressed(_ sender: Any) {
if contact.text?.isEmpty == true {
print("No contact")
return
}
if avail.text?.isEmpty == true {
print("No availability")
return
}
uploadSpot()
}
func uploadSpot() {
let key = Int.random(in: 20001..<30000)
let object: [String: Any] = [
"Contact": contact.text!,
"Availability": avail.text!,
]
geoFireRef.child("\(key)").setValue(object)
let location = manager.location!
geoFire.setLocation(location, forKey: "\(key)")
if interstitialAd?.isReady == true {
interstitialAd?.present(fromRootViewController: self)
}
let storyboard = UIStoryboard(name: "SpotLoaded", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "SpotLoaded")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
}
The import part is in the function "uploadSpot()," where
if interstitialAd?.isReady == true {
interstitialAd?.present(fromRootViewController: self)
}
presents the ad and
let storyboard = UIStoryboard(name: "SpotLoaded", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "SpotLoaded")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
switches the viewcontroller. I have tried reversing the order if these two (i.e. putting let storyboard, etc., before if insterstitialAd), but that just takes me to the new view controller and the ad never appears. I have also tried putting the let storyboard chunk inside of the brackets following the if interstitialAd. This does not help either. If I put it after interstitialAd?.present(fromRootViewController: self), I have the same problem where the ad shows but the view controller does not change. If I put it after, the viewcontroller changes but the ad never shows.
As the main purpose of interstitial ads is to appear in between views of an app, I do not think what I am trying to do should be so difficult. I think there must just be something I am missing or do not understand. Please help in whatever way you can.

Open first View based on condition

I want my app to check at start conditionaly if a variable is correct or not. Based on that it should either go to an intro screen (where he can select a variable in my case select a team) or it should start the main view. After searching i found this code and edited it.
But there still seems to be problems. First of all I dont have two identifier. The Intro has one but not the main view. My main View is called WeatherViewController and the Intro screen is called FirstScreenViewController.
I also added a picture of my Main.storyboard.
I also googled a lot about conditional UINavigationController but I can only understand with a video and did not found a video about it.
I tried to use the code from here.
var id = hello ? "goToIntro" : "???"
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let WeatherViewController: UIViewController = mainStoryboard.FirstScreenViewController(withIdentifier: WVC has no identifier??) as UIViewController
self.window?.rootViewController = WeatherViewController
self.window?.makeKeyAndVisible()
if hello {
self.performSegue(withIdentifier: "goToIntro", sender: self)
} else {
/here nothing should happen. It should open the Main View
self.performSegue(withIdentifier: "???", sender: self)
}
Simply repeat the steps you did for creating the segue goToIntro.
Add a new UIViewController (MainViewController) in your storyboard.
Add a segue and give it an identifier.
Provide the identifier value in the else condition of the code that you've shown in your post to perform the segue to the UIViewController that you've just created.
The final code should look something like this:
var id = hello ? "goToIntro" : "goToMain"
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let weatherViewController = mainStoryboard.instantiateInitialViewController()
window?.rootViewController = weatherViewController
window?.makeKeyAndVisible()
weatherViewController.performSegue(withIdentifier: id, sender: self)
You don't need the if else block since the id is already determined using the ? ternary operator.

how to change UITabBarItem's Title dynamically

I have a TabBarController in my App, and the 2nd item should have title Login or Profile depending on whether the user is logged in or not.
in TabBarController:
import UIKit
class TabNavigationBarVC: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
self.delegate = self
if UserDefaults.standard.bool(forKey: "isUserLoggedIn") {
tabBar.items?[1].title = "Prof"
tabBar.items?[1].image = UIImage(named: "user_male")
} else {
tabBar.items?[1].title = "Log"
tabBar.items?[1].image = UIImage(named: "user_male")
}
}
}
and 2nd TabBarItem is connected with RouterVC:
import UIKit
class RouterViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
let profile = storyboard?.instantiateViewController(withIdentifier: "ComponentUserProfile") as? UserProfileViewController
let login = storyboard?.instantiateViewController(withIdentifier: "ComponentLogin") as? LoginViewController
if UserDefaults.standard.bool(forKey: "isUserLoggedIn") {
//profile?.tabBarItem.title = "prr" - no effect
//profile?.tabBarItem = UITabBarItem(title: "PROFILE", image: UIImage(named: "user_male"), tag: 0) - no effect
viewControllers = [profile] as! [UIViewController]
} else {
//login?.tabBarItem.title = "logg"- no effect
//login?.tabBarItem = UITabBarItem(title: "LOGIN", image: UIImage(named: "user_male"), tag: 0)- no effect
viewControllers = [login] as! [UIViewController]
}
}
}
all works correctly but only when the user launches the app. Later, after the user has logged out, I would like the 2nd title in the tabBar to change from Profile to login, or - when the user is successfully logged in - from Login to Profile. I tried something like commented lines of code in RouterVC, but nothing changed.
And in LoginVC this lines not working too:
override func viewWillAppear(_ animated: Bool) {
self.tabBarItem.title = "loggggg"
}
How can I make this change dynamically?
Maybe I should write extension for TabBarController to track if UserDefaults.standard.bool(forKey: "isUserLoggedIn") is changed and display correct title of the tabbarItem?..
p.s.navigation between views, login/logout works good, the point is only in title
p.p.s - find the answer how get access to the title from view - add self.tabBarController?.tabBar.items?[1].title = "profile", but still looking for some common decision, like tracking UserDefaults key..
you can update the selected Item title using
self.tabBarController?.tabBar.selectedItem?.title = "demo"

Change ViewController from Tabbar

I have a UITabBarController with 3 tabs and a SettingsVC where you can choose the appearance of MainVC (There are 2 different ViewControllers which based on what user prefers should be shown as the MainVC).
I made the application and it works but it's really buggy, cause I was just pushing the ViewControllers on top of each other, and in some certain conditions you see bugs while switching tabs.
Main.storyboard
Here is some part of my code(after trying a lot of stuff) which changes the VC's based on UserDefaults
CheckSettingsStatus.swift
//
// Check for Double Column Settings and navigate to Double Column VC
//
func checkMainVCViewStatusAndNavigateAcordingly(){
if UserDefaults.standard.integer(forKey: KEY_MAIN_VC_VIEW) == 1{
//let appDel = UIApplication.shared.delegate as! AppDelegate
//appDel.goToDoubleMainVC()
let mainVC = storyboard?.instantiateViewController(withIdentifier: "DoubleColumnMainVC") as! DoubleColumnMainVC
self.navigationController?.viewControllers[0] = mainVC
//navigationController?.pushViewController(mainVC, animated: false)
var navigationArray = self.navigationController?.viewControllers
print("Number of VCs = \(String(describing: navigationArray?.count))")
if navigationArray?.count == nil{
self.navigationController?.pushViewController(mainVC, animated: false)
}else{
self.navigationController?.popToRootViewController(animated: false)
}
// self.navigationController?.viewControllers = navigationArray!
}
}
//
// same as function above but goes to MainVC
//
func checkDoubleColumnMainVCViewStatusAndNavigateAcordingly(){
if UserDefaults.standard.integer(forKey: KEY_MAIN_VC_VIEW) == 0{
// let appDel = UIApplication.shared.delegate as! AppDelegate
// appDel.goToMainVC()
let mainVC = storyboard?.instantiateViewController(withIdentifier: "MainVC") as! MainVC
self.navigationController?.viewControllers[0] = mainVC
var navigationArray = self.navigationController?.viewControllers
print("Number of VCs = \(String(describing: navigationArray?.count))")
if navigationArray?.count == nil{
navigationController?.pushViewController(mainVC, animated: false)
}else{
self.navigationController?.popToRootViewController(animated: false)
}
// self.navigationController?.viewControllers = navigationArray!
}
}
With this in ViewWillAppear of both this Controllers I call these functions to switch accordingly.
Any ideas what I'm doing wrong? Can I push controllers from UITabBarController and not NavigationController? something like:
tabBarController.pushViewController(mainVC, animated: false)
instead of:
navigationController.pushViewController(mainVC, animated: false)
Thank you in advance
UPDATE:
In my best case when everything works nicely, on first launch of the app (to the second mainVC) the Buttons in NavBar doesn't work.
EDIT:
I just realised that the NavigationBar that I'm seeing above my SecondMainVC is the NavigationBar from MainVC, that is why the buttons are not working. how is that possible?
I faced same issue while trying to develop a tab bar controller in storyboard , for this reason I found that the best way is to implement it by code.
so you can make your code look like this :
1) create a a sub class of UITabController and let it conform to UITabBarControllerDelegate
class TabBarVC: UITabBarController , UITabBarControllerDelegate {
2)then write this method in your class to initialize the tabs in your project:
private func settingUpTabBarAndInitializingViewControllers(){
//self delagting
self.delegate = self
//home tab
let homeStoryboard = UIStoryboard(name: Constants.StoryBoards.homeStoryboard, bundle: nil)
let homeVC = homeStoryboard.instantiateViewController(withIdentifier: Constants.ControllersIDs.homeScreen) as! HomeVC
// shop tab
let shopStoybaord = UIStoryboard(name: Constants.StoryBoards.shopStoryboard, bundle: nil)
let shopVC = shopStoybaord.instantiateViewController(withIdentifier: Constants.ControllersIDs.shopScreen) as! ShopVC
//offers tab
let offersStoryboard = UIStoryboard(name: Constants.StoryBoards.offersStoryboard, bundle: nil)
let offersVC = offersStoryboard.instantiateViewController(withIdentifier: Constants.ControllersIDs.offersScreen) as! OffersVC
//more tab
let moreStoryboard = UIStoryboard(name: Constants.StoryBoards.MoreScreenStoryboard, bundle: nil)
let moreVC = moreStoryboard.instantiateViewController(withIdentifier: Constants.ControllersIDs.moreVCScreen) as! MoreOptionsTVC
//setting VCs
self.viewControllers = [homeVC , shopVC , offersVC , moreVC]
//setting buttons
//buttons images
let homeImage = UIImage(named: "TabHome")
let shopImage = UIImage(named : "TabShop")
let offerImage = UIImage(named: "TabOffers")
let moreImage = UIImage(named: "TabMenu")
// buttons shakhsiyan
let homeButton = UITabBarItem(title: homeText, image: homeImage, selectedImage: homeTappedImage)
let shopButton = UITabBarItem(title: shopText, image: shopImage, selectedImage: shopTappedImage)
let offersButton = UITabBarItem(title: offersText, image: offerImage, selectedImage: offersTappedImage)
let moreButton = UITabBarItem(title: moreText, image: moreImage, selectedImage: moreTappedImage)
homeVC.tabBarItem = homeButton
shopVC.tabBarItem = shopButton
offersVC.tabBarItem = offersButton
moreVC.tabBarItem = moreButton
}
P.S. you can add tabs as much as you want and while scrolling in them you wont face any bug,
make sure to have only one navigation controller before your tab bar controller.
Check this hope it helps
Ok you can create a DEMO to see how thing works.After that you can implement it in your project.I know it may not look good to you but read it once and see image attached i am sure will get your answer.
1.I have created two UIViewControllers with the UITabBarController.
2.I have created class ItemFirstVC,ItemTwoVC and assigned to UIViewControllers in storyboard
3.Each controller is embedded in UINavigationController
4.Create a custom class of UITabBarController
5.Assign it to UITabBarController in storyboard
6.Now Each UIViewController has a button named as Firs Button,Second Button
7.From each button i have connected Action Segue to one more controller as you can see in image.
8.Now i have created a third UIViewController which is separate from UITabBarController.
9.Now we will load that Third UIViewController in UITabBarController
10.Create one more class ItemThirdVC and assign it to third UIViewController.
11.It also has a button named as third button action segued to one more UIViewController
12.Now switch to custom class of UITabBarContoller
// variable required to see work in action
var count = 0
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// remember to assign delegate
delegate = self
}
// this delegate indicates whether this tab should be selected or not
extension CustomTabBarController : UITabBarControllerDelegate{
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
// switch to UIViewController tabs multiple times
count = count + 1
if(count == 5){
// when count reaches 5 a new controller will load
let navigationController = viewController as? UINavigationController
guard navigationController != nil else {return true}
let viewController = storyboard?.instantiateViewController(withIdentifier: "ItemThreeVC")
guard let vc = viewController else {return true}
navigationController?.setViewControllers([vc], animated: false)
}
return true
}
}

How to call viewcontroller method from AppDelegate if I use TabBar Navigation

I really need a help with calling view controller method as soon as my app open when the remote notification comes in especially when using TabBar navigation.When I open my app when the notification arrive,it will call didReceiveRemoteNotification because I enabled "content-available = 1".But,the problem is I don't know how to access my HomeViewController method refresh() because I use TabBar Navigation.I need to call HomeViewController refresh() method inside didReceiveRemoteNotification because it really need to refresh the data from core and the notification that arrived are saved at coredata.
Everytime the notification comes in,I save them at realm database and show user at HomeViewController
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) {
if let aps = userInfo["aps"] as? NSDictionary{
if let alert = aps["alert"] as? NSDictionary{
if let mbody = alert["body"] as? String{
print("Message Body : \(body)")
body = mbody
}
if let mtitle = alert["title"] as? String{
print("Message Title : \(title)")
title = mtitle
}
}
}
let newNotification = NotificationList()
newNotification.title = title
newNotification.body = body
//Realm insert query
oneSignalHelper.insertOneSignalNotification(newNotification)
print("Remote Receive")
//This condition isn't working
if let viewController = self.window?.rootViewController as? HomeViewController {
print("Remote Receive Read")
//This method is HomeViewController method which I gonna use for refresh when I tapped notification.It read that are save in realm database and show results.
viewController.readNotificationsAndUpdateUI()
}
handler(UIBackgroundFetchResult.NewData)
}
This is readNotificationsAndUpdateUI() from HomeViewController
func readNotificationsAndUpdateUI(){
notifications = realm.objects(NotificationList)
self.notificationTableView.setEditing(false, animated: true)
self.notificationTableView.reloadData()
self.refreshControl?.endRefreshing()
}
I suggest you using NSNotificationCenter, just post notification inside didReceiveRemoteNotification like this:
NSNotificationCenter.defaultCenter().postNotificationName("refreshNotification", object: nil)
In your HomeViewController in viewDidLoad() you should subscribe to the notification center like this:
NSNotificationCenter.defaultCenter().addObserver( self, selector: "refresh", name: "refreshNotification", object: nil)
Not sure if it helps but I solved it this way, based on previous answer
Load Storyboard
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
Load ViewController (check that you have an identifier in your storyboard for this ViewController!)
let bestellvorschlag = storyBoard.instantiateViewController(identifier: "Bestellvorschlag") as! Bestellvorschlag
Load Function on ViewController
bestellvorschlag.loaddata() //Loads CoreData into Array of artikels
Maybe Modify the Tabbar Badge with the number of Results
let tabbarcontroller = self.window?.rootViewController as! UITabBarController
if bestellvorschlag.artikels.count > 0
{
if let tabItems = tabbarcontroller.tabBar.items {
// Third Tab!
let tabItem = tabItems[2]
tabItem.badgeValue = bestellvorschlag.artikels.count.description
}
}
Complete Function in appDelegate
func applicationDidBecomeActive(_ application: UIApplication) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let bestellvorschlag = storyBoard.instantiateViewController(identifier: "Bestellvorschlag") as! Bestellvorschlag
bestellvorschlag.loaddata()
let tabbarcontroller = self.window?.rootViewController as! UITabBarController
if bestellvorschlag.artikels.count > 0
{
if let tabItems = tabbarcontroller.tabBar.items {
// Third Tab!
let tabItem = tabItems[2]
tabItem.badgeValue = bestellvorschlag.artikels.count.description
}
}
}