Access to tabbar Controller from AppDelegate - uitabbar

I have this situation:
How can I access the 3rd Tab bar badge from AppDelegate?
I've used this code:
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UIViewController *vc = [[UIViewController alloc] init];
if ([nonLette isEqualToString:#"0"]) {
for (vc in tabBarController.viewControllers) {
if (vc.tabBarItem.tag == 999) {
vc.tabBarItem.badgeValue = nil;
}
}
but the self.window.rootViewController returns "start View" and so does not work.

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabController = storyboard.instantiateViewController(withIdentifier: "TabBarViewController") as! TabBarViewController
let tabArray = tabController.tabBar.items as NSArray!
let alertTabItem = tabArray?.object(at: 3) as! UITabBarItem
alertTabItem.badgeValue = "10"

Ok, I'm resolved, but in part!!!
This method works well. With debug, I can see that now I can change the badge on desired tab, but in reality don't change in graphics (the red number not appear).
Some suggestion?
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UITabBarController *myTabBarController = [storyboard instantiateViewControllerWithIdentifier:#"tabBarController"];
if ([nonLette isEqualToString:#"0"]) {
for (UIViewController *vc in myTabBarController.viewControllers) {
if (vc.tabBarItem.tag == 999) {
vc.tabBarItem.badgeValue = nil;
}
}
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
} else {
for (UIViewController *vc in myTabBarController.viewControllers) {
if (vc.tabBarItem.tag == 999) {
vc.tabBarItem.badgeValue = nonLette;
}
}
[UIApplication sharedApplication].applicationIconBadgeNumber=[result integerValue];
}

With the new XCode 11 and Swift 5 this is no longer possible.
Instead you want to do this in SceneDelegate.swift, in particular this function func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
You then want to do something like this.
self.window = self.window ?? UIWindow()//#JA- If this scene's self.window is nil then set a new UIWindow object to it.
//#Grab the storyboard and ensure that the tab bar controller is reinstantiated with the details below.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! UITabBarController
self.window!.rootViewController = tabBarController //Set the rootViewController to our modified version with the StateController instances
self.window!.makeKeyAndVisible()
print("Finished scene setting code")
guard let _ = (scene as? UIWindowScene) else { return }
Hope this helps for the people using the newer XCode out there. The key here is appDelegate no longer has access to window, the sceneDelegate does. If you try to use appDelegate you will get an error that window no longer exists.
Make sure the identifier is set for the tabBarController in the storyboard so it can find it. You can name it whatever, but just refer to that name in the code here for the withIdentifier function

Related

Share JSON Data in TabBarController to view controllers

I am using a tabbarcontroller to show 3 xib's. I would like to decode JSON data in the UITabBarController subclass, and then share the data with the view controllers (as I understand that is the preferred way to do this). I had already successfully accomplished this individually in each view controller, where the same JSON data was getting decoded separately 3 times, but I am now trying to make the process more efficient by only dealing with JSON once.
I am currently getting the following error
"Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee7ab7d98)".
Below is the code I am currently using. I'm mostly only including the code for the first view controller, but it is the same for the others
Here is one of the view controllers. Any help would be appreciated, thank you!
class FirstCollectionViewController: UIViewController {
var tbvc = CustomTabBar()
var statisticsData = [Model]()
let firstCellIdentifier = "FirstCellIdentifier"
#IBOutlet weak var FirstCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
FirstCollectionView.delegate = self
FirstCollectionView.dataSource = self
FirstCollectionView.register(UINib(nibName: "FirstCollectionViewCell", bundle: nil),forCellWithReuseIdentifier: firstCellIdentifier)
}
}
Here is the subclasses UITabBarController
import UIKit
class CustomTabBar: UITabBarController {
let website = "https:......."
var statisticsData = [Model]()
override func viewDidLoad() {
super.viewDidLoad()
let firstTab = FirstCollectionViewController(nibName: "FirstCollectionViewController", bundle: nil)
let secondTab = SecondCollectionViewController(nibName: "SecondCollectionViewController", bundle: nil)
let thirdTab = ThirdCollectionViewController(nibName: "ThirdCollectionViewController", bundle: nil)
viewControllers = [firstTab, secondTab, thirdTab]
downloadJSON(website: website) {
firstTab.statisticsData = self.statisticsData
secondTab.statisticsData = self.statisticsData
thirdTab.statisticsData = self.statisticsData
firstTab.FirstCollectionView.reloadData()
secondTab.SecondCollectionView.reloadData()
thirdTab.ThirdCollectionView.reloadData()
}
}
func downloadJSON(website:String, completed:#escaping ()->()){
guard let qurl = URL(string: website) else { return }
URLSession.shared.dataTask(with: qurl) { (data, response, error) in
if error == nil {
do{
self.statisticsData = try JSONDecoder().decode([Model].self, from: data!)
DispatchQueue.main.async{
completed()
}
} catch {
print("JSON Error")
}}
}.resume()
}
}
Once the data is loaded, you should assign the data to the viewControllers that are added in the tabBarController's Child list as below,
downloadJSON(website: website) {
firstTab.statisticsData = self.statisticsData
secondTab.statisticsData = self.statisticsData
thirdTab.statisticsData = self.statisticsData
firstTab.FirstCollectionView.reloadData()
secondTab.SecondCollectionView.reloadData()
thirdTab.ThirdCollectionView.reloadData()
}
You can also remove the below lines from viewDidLoad of FirstCollectionViewController, SecondCollectionViewController and ThirdCollectionViewController
tbvc = tabBarController as! CustomTabBar
statisticsData = tbvc.statisticsData

Redirect to specific view

I want to redirect the user of my app to a specific view if he is logged in or not. This is my code:
override func viewDidLoad() {
super.viewDidLoad()
if FIRAuth.auth()?.currentUser != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"LoginViewController") as! LoginViewController
self.present(viewController, animated: true)
}
else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"SWRevealViewController") as! SWRevealViewController
self.present(viewController, animated: true)
}
}
If I open my app I always get a white screen.
Does anyone know my mistake?
It worked. I didn't connected the navigation controller in the storyboard. The code itself works finde.

Instantiate UIViewController from Storyboard, viewDidLoad() not executed

I'm creating a Slideout navigation controller.
Now I need to assign my MenuViewController and my TopViewController.
I have a TopViewController screen in a Storyboard that was using other type of slideout menu.
In my AppDelegate I have the following:
let storyboard : UIStoryboard! = UIStoryboard(name: "Main", bundle: nil)
let dashboard = storyboard.instantiateViewControllerWithIdentifier("DashboardView") as! DashboardViewController
let menu : SlideoutViewController = SlideoutViewController()
menu.menuEnabled = true
menu.topView = dashboard
menu.menuView = SalesViewController()
UIApplication.sharedApplication().keyWindow?.rootViewController = menu as SlideoutViewController
But when doing this the viewDidLoad method in DashboardViewController is never reached, that means that the view has not been instantiated yet.
Any clue?
UPDATE: this is a function selectView in slideout view controller
func selectView(viewController: UIViewController){
if (_internalTopNavigation != nil)
{
_internalTopNavigation!.removeFromParentViewController();
_internalTopNavigation!.view.removeFromSuperview();
}
_internalTopNavigation = UINavigationController(rootViewController: viewController)
_internalTopNavigation!.view.frame = CGRect(x: 0, y: 0,
width: _internalTopView!.view.frame.width,
height: _internalTopView!.view.frame.height)
_currentView = viewController;
_internalTopView!.addChildViewController(_internalTopNavigation!);
_internalTopView!.view.addSubview(_internalTopNavigation!.view);
if (_menuEnabled == true)
{
viewController.navigationItem.leftBarButtonItem = createMenuButton()
viewController.navigationItem.leftBarButtonItem!.tintColor = UIColor.blueColor()
}
_externalContentView = viewController;
}

swift: how to pass JSON to secondViewController

I want to pass the JSON data to MainMenuPageViewController.
MainMenuPageViewController UILabel(UsernameLabel)
Thank you
Picture1:
let jsonUserId: String = json["return"] as! String
if (jsonUserId != "0") {
print("username and password correct")
dispatch_async(dispatch_get_main_queue(), {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("MainMenuPageViewController")
self.showViewController(vc, sender: self)
//pass the jsonUserId to MainMenuPageViewController
//MainMenuPageViewController has UILabel(UserIdLabel)
})
}
If I input wrong username and password, system will run to line 57 but the alert will error, when I input long String(a,A,#,etc.). However, if I input shot String(shing, herry,123,etc.) app can display the alert message.
Also, if input the space bar and (!##$%^&*()_+). it will error.
Can you help me to fix this error? Thank you.
Just declare variable in MainMenuPageViewController like
class MainMenuPageViewController: UIViewController {
var UserIdLabel = String()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
and for pass data
let jsonUserId: String = json["return"] as! String
if (jsonUserId != "0") {
print("username and password correct")
dispatch_async(dispatch_get_main_queue(), {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyboard.instantiateViewControllerWithIdentifier("MainMenuPageViewController") as? MainMenuPageViewController {
vc.UserIdLabel = jsonUserId
self.showViewController(vc, sender: self)
}
})
}
You should create a property in the MainMenuPageViewController to store the user id and then in viewDidLoad set the UsernameLabel text to the stored property.
var userID:String! //define this in the MainMenuPageViewController
Then after you create the vc set the userID property to the jsonUserID
if let vc = storyboard.instantiateViewControllerWithIdentifier("MainMenuPageViewController") as? MainMenuPageViewController {
vc.userID = jsonUserId
self.showViewController(vc, sender: self)
} else {
printf("The VC is not of the right type")
}
override func viewDidLoad() {
super.viewDidLoad()
UsernameLabel.text = userID
}

passing info from collectionView to gameScene

I have created a game using SpriteKit (GameScene and GameViewController) and Objective-C. Everything there works fine.
Within the same App I have created a UIViewController and a second viewController in storyboard that uses CollectionView. I have the collectionView loading the array.
I want to be able to tap on a collectionView Cell and have it open the gameView. Which I can do. However I would like to pass some information from the collectionView to the GameScene so I can load the background image and other information to individualize the GameScene for each collectionViewCell.
Here is my code for the collectionView
#import "collectionViewController.h"
#import "GameScene.h"
#import "GameViewController.h"
#implementation collectionViewController
#synthesize animalArray,theImage,StringPicture,myCell;
-(void) viewDidLoad {
[super viewDidLoad];
animalArray = [NSArray arrayWithObjects:#"Cat.png",
#"image1.png",
#"image2.png",
#"image3.png",
,nil];
}
-(NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return animalArray.count;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
myCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
UIImage *images;
long row = [indexPath row];
images = [UIImage imageNamed:animalArray[row]];
myCell.image1.image = images;
return myCell;
}
-(void) collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"indexPath.row:%ld",(long)indexPath.row);
if (indexPath.row==1) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"GameSceneOne"];
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
NSLog(#"mycell.image1.image:%#",myCell.image1.image);
[self presentViewController:vc animated:YES completion:NULL];
}
if (indexPath.row==2) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"GameSceneOne"];
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:vc animated:YES completion:NULL];
}
}
I have tried assigning the GameScene.image to the vc.image but one is a GameView and the other is a UIViewController so I can't do that.
I have tried using a segue but still the information can't be passed from on to the other.
How should I do this?
I approached this in a different way. Basically:
Cell has the skview and loads and presents scene.
Scene exposes properties (like a label for the months in the example)
In the cellForItemAt of the collectionView datasource, I instantiate the scene and just use the exposed properties.
Here is in code:
Step 1:
class GameCell: UICollectionViewCell {
#IBOutlet weak var myskview: SKView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
if let scene = SKScene(fileNamed: "MyScene") as? MyScene{
scene.scaleMode = .aspectFill
myskview.presentScene(scene)
}
}
}
Step 2:
class MyScene: SKScene {
var gameLabel: SKLabelNode!
var monthValue: String?
var bird: SKSpriteNode?
var floor: SKSpriteNode?
var bgColor: UIColor?
let birdCategory: UInt32 = 0x1 << 0
let floorCategory: UInt32 = 0x1 << 1
override func didMove(to view: SKView) { }
Step 3:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GameCell", for: indexPath) as! GameCell
let scene = cell.myskview.scene as! MyScene
scene.gameLabel.text = monthComponents[indexPath.row]
Just don't forget to create the label inside the scene using:
gameLabel = scene?.childNode(withName: "gameLabel") as! SKLabelNode
You can check the final result in my git: https://github.com/tennydesign/spriteKitAndCollectionView
Just pardon my dust. =)
Couple of ways to do this. One of them is using NSUserDefaults.
To store data in NSUserDefaults, do this:
// object can be almost anything. Strings, arrays, dictionaries...
NSString *object = #"WellDone";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:object forKey:#"Hamburger"];
[defaults synchronize];
To read NSUserDefaults, do this:
NSString *myString = [[NSUserDefaults standardUserDefaults] objectForKey:#"Hamburger"];
The key being that you use the same key as when you created it.
And of course, read the docs (blah,blah, blah).