Reload tabBarController after switching language - ios

In the settings section of my app the user has the option to change the language of the app. So when the user chooses spanish as his primary language the app show the content in spanish after he did an app restart but I want to change the language on the fly. This works for the main content like a TableView because I simply can reload the data but the language in my TabBarController does not change because I don't know how.
So I want to update (or better call it a reset) the TabBarController. After the reset it should display all navigation points in the new language.
My idea was to remove the current TabBarController and initialize a new one. Is this possible? Or is there a better way?

I am not an native english speaker so if my explanations aren't clear enough, just tell me and I'll try to rephrase them.
It might look scary and complicated because of my long post, But it really isn't, it is just long because I thought it would be better to also explain how to do it, instead of just giving a few lines of code.
You can achieve what you want using UITabBarController properties.
UITabBarController have a property called tabBar, which is the actual UITabBar.
One might think that in order to achieve what you want, you should edit this property,
HOWEVER, editing this property would cause an exception.
From apple's UITabBarController documentations, regarding the tabBar property:
You should never attempt to manipulate the UITabBar object itself stored in
this property. If you attempt to do so, the tab bar view throws an exception.
So you should never attempt to edit this property at runtime.
After that word of warning, here is what you should do-
UITabBarController also have a property called viewControllers, which is an NSArray who holds reference to the view controllers that being displayed by the tab bar.
This property CAN be modified at runtime, and changes applied to it are updated instantly in the tab bar.
However, for your case, you don't need to modify this property,
But I thought you should know that so if in some situation you will need to add or remove some items from your tab bar, you'll know that can do it.
What you do want to do, is iterate through the objects of that array to access the view controllers themselves.
UIViewController have a property called tabBarItem which represents the UITabBarItem of the view controller.
So what we are basically doing, is getting the tab bar item of the view controller, but instead of getting it from the UITabBarController itself, we are getting it directly from each view controller.
Each UITabBarItem has a title property, and this is what you want to change.
So now, after that long introduction, let's get to the actual code.
I think a pretty easy way to achieve what you want is to iterate thru the viewControllers array, and have some switch statement in there that would change the title.
As in any programming situations, this can be done in countless other ways, so you might have a better way to implement it than my example below, but this should do the trick.
Each view controller that being displayed in a tab bar controller, have a reference to that tab bar using the property tabBarController
So you can run this code in any of the view controllers that being displayed in the tab bar, and simply use self.reference to get a reference to it.
Add this somewhere after the language have changed-
for (int i = 0; i < [self.tabBarController.viewControllers count]; i++) {
if([self.tabBarController.viewControllers[i] isKindOfClass: [UIViewController class]]) {
UIViewController *vc = self.tabBarController.viewControllers[i];
switch(i) {
case 0:
vc.tabBarItem.title = #"primero";
break;
case 1:
vc.tabBarItem.title = #"secondo";
break;
}
}
}
What we are basically doing, is running a for loop that iterating thru all of the items in the array,
The items in the array are in the same order that they appear on the tab bar,
then we use a switch statement to change the title for the view controller in the corresponding position,
Since array have index 0, the first view controller is at position i=0 and the last one is at one less than the count of items in the array.
Some might argue that my if is unnecessary,
Since we already know that this array holds only view controllers, there is no need to check if the item at that position is of UIViewController class, or a subclass of it.
They might be right, but I always say it's better to be safe than sorry.
Of Curse I would also include in your code something to actually check to what language the user have chosen.
The example above changes the titles to spanish, regardless of the user's choice.
Hope it helps mate,
Good luck

#AMI289 gave a good idea.
Make an extension for UITabBarController and do a loop there. Can call anywhere from the tabBarControllers stack.
In my case after the tabBarController goes navigationController.
// MARK: - UITabBarController
extension UITabBarController {
func changeTitleLocale() {
guard let viewContollers = self.viewControllers else { return }
for (index, navVC) in viewContollers.enumerated() {
if let view = navVC as? UINavigationController {
if let topView = view.topViewController {
if topView.isKind(of: ProfileVC.self) {
self.tabBar.items?[index].title = "tab_profile"
} else if topView.isKind(of: ChatVC.self) {
self.tabBar.items?[index].title = "tab_chat"
} else if topView.isKind(of: PicturesVC.self) {
self.tabBar.items?[index].title = "tab_pictures"
} else if topView.isKind(of: VideosVC.self) {
self.tabBar.items?[index].title = "tab_videos"
}
}
}
}
}
}
Then, when we change a language just run it:
self.tabBarController?.changeTitleLocale()

Related

Setting badge on a tabbar item of chosen type (without knowing its index)

I have a tabBar in my app. I want to display a badge with a number of unread messages when notification comes. So far my code is below.
The problem is that the messages tab isn't always the first item in the list (the order varies for different app settings, but it's always there). How do I set the badge on it if I don't know which index it has?
if let item = self.tabBar.items?.first {
var count = messages.count
if item.badgeValue != nil {
count += Int(item.badgeValue!) ?? 0
}
item.badgeValue = "\(count)"
}
You are getting first item in first line - so it's working as expected.
You need to organize your UITabBar this way, that you can always identify on which index messages are presented.
One idea would be to keep a reference to it when you configure your UITabBar - this way that you can always find under which index messages are shown.
Best way would be to keep corresponding array of views you keep behind your UITabBar, and then find the one you need.
If you are using UITabBarController you will get it for free - all UIViewController are accessible directly via property named viewControllers.
If you have custom ViewController and just UITabBar - you just need to build similar logic that will allow you to keep track on which index certain view controller is shown.
Assuming your tabbar is managed by a UITabBarController, you can determine the index by checking the type of the view controllers in your tab bar controller.
class MessagesViewController: UIViewController {
// ... your messages vc
}
if let index = tabbarController.viewControllers?.firstIndex(where: { $0 is MessagesViewController }) {
tabbarController.tabBar.items?[index].badgeValue = "..."
}

swift; xcode 9.2 - Passing arguments over TabBar & navigation controller

In my storyboard I got:
UIView -> UITabBarController -> UINavigationController -> UITableView
Now I want to pass an object from UIView into UITableview. I do get the object to the TabBarController from the prepare for segue func, but from there I kind of get lost.
How to identify what segue you have on the itemlist from the TabBarController?
Could somebody give some example code for the UITabBar and Navigation controller to pass the data?
Phillip is right.
You can do it as following:
class Model {
static let shared = Model()
var data: String // or anything else
}
in UIView:
Model.shared.data = "some data"
in UITableView
let data = Model.shared.data
//do smth with data...
Anton is suggesting the Singleton pattern. It is important to understand what it is when you decide to use it has both its benefits and potential pitfalls. https://thatthinginswift.com/singletons/ is a place to start reading up.
There are ways to just pass an object from one view to the other and that is useful knowledge to know. Both TabBarVC's and NavigationVC's have their viewControllers property which allows you to access an array of their child vc's. You can use this to pass information to specific child vc's. Depending on your needs this may be more appropriate than creating a singleton.
For example:
let childVC = tabBarVC.viewControllers[0] as! MyCustomVCClass
childVC.inheretedObject = objectIWantToSend
This would pass an object to the vc that ocupies the first tab of a tab bar vc.

TabBarController behind the scenes and correct approach for code behind

Just imagine( as case) that i have TabBarController as root and several ViewControllers that are linked to this TabBarController( via Storyboard(!) that is important step,i mean programmatically it's something other).
And its works without any code behind on TabBarController class!
So i'm interested in right approach, how can i Inistiate Viewcontrollers inside TabBarController on right way!?
Or it's isn't necessary and OS will do it for me , because at current moment, i can do something like this :
public override void ViewDidLoad()
{ //No Init for VC that are inside linked to TabBAr
base.ViewDidLoad();
//Find all viewcontrollers that are linked in storyboard
foreach (var Item in ViewControllers)
{
Item.TabBarItem.Image =
UIImage.FromBundle("RandomImg");
//Change title for all VC to "Main"
Item.TabBarItem.Title = "Main";
break;
}
}
What if i will do initialization of ViewControllers inside TabBarController constructor.
What will be happened on behind the scenes? How the system will recognize my init of VC,or it's doesn't matter and thats will be an redundant action, because technique via storyboard anyway creates ViewControllers without any initialization on code behind!?
How to handle/customize correctly ViewControllers that are inside of TabBarController?(e.g. select another VC at beginning, change buttons on TabBar and etc)
I would like to take all answers on all my questions and if it possible, need an live example/article that explains more advanced about this stuff. Thanks!

iOS - How to use a small view in different view controllers in Swift

I have a progress bar (with its own controller). This bar is supposed to be shown in different views depending on which view is visible. As the progress will be same, If possible I don't want to create many progress bar in many views rather I want to use same instance in all these views. Also in that way when I need to change any property of the progress bar it will be reflected commonly, which is required.
Please suggest me how can I use this common view. And also if my strategy is wrong, what would be the better design for such scenarios.
1) Well you have 2 options. You can declare a new Class ViewBox (or whatever name) and then use that inside your code
First View Controller
var box:ViewBox = ViewBox()
When you segue or transition to your next screen, you can have a predefined variable var box:ViewBox!. Then say when you press a button, the button has a function called transition.
//Now setup the transition inside the Storyboard and name the identifier "toThirdViewController"
override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {
if(segue.identifier == "toThirdViewController") {
var vc = segue.destinationViewController as! `nextViewController` //The class of your next viewcontroller goes here
vc.box = self.box
}
//Since The SecondViewController doesn't need ViewBox, we don't need it there.
}
where
nextViewController:UIViewController {
var box:ViewBox!
}
Or you could do a much simpler way and that is to look up a UIPageViewController :)

Split view in container view loses function of navigation controller

i'm building an app for which i need a split view controller. I embedded it in a container view so i could use a segue to acces it. The problem now is that I can't use my navigation bar in a proper way anymore. Normally, there should be a button to go to the previous page, but now I only have the option to go to the homepage (for within the split view controller). You can find some images here. Also, the title will always be "Shoppen", even though I stated it otherwise.
I suppose the problem is that the navigation bar of the view controller in which the container view is set will always be the top one. I have no idea on how to either delete or fix this problem though, could anybody here help me with this? Thanks in advance!
Some code to show you what it should normally do:
In the first section, the table view, it should indeed always title "Shoppen" and able to go back to the homepage
In the detail section, we should be able to go to the table view page, and the title should be the category of the shop we clicked. This is defined here:
func configureView() {
if let detailWinkel = detailWinkel {
if let detailDescriptionLabel = detailDescriptionLabel, WinkelImageView = WinkelImageView {
if detailWinkel.name == "Hunkemoller" {
detailDescriptionLabel.text = "Hunkemöller"
} else {
detailDescriptionLabel.text = detailWinkel.name
}
WinkelImageView.image = UIImage(named: detailWinkel.name)
title = detailWinkel.category
StraatDescriptionLabel.text = detailWinkel.straat
AdresDescriptionLabel.text = detailWinkel.adres
WebsiteButton.setTitle("Open website in Safari", forState: UIControlState.Normal)
}
}
}
I don't completely understand what you are trying to do.
An embed segue is not a normal segue. You can't push a view that is embedded into another view controller onto a navigation stack, if that's what you are saying.
An embed segue is a degenerate case. It invokes the contained view controller at the time the parent view controller is loaded, and there is no mechanism for going back.

Resources