Change TabBar Items after changing app language from inside of app - ios

I have an app and from inside of it I am changing its language.
Language getting properly updated to other screens by changing UIView appearance.
Localize.setCurrentLanguage("ar")
UIView.appearance().semanticContentAttribute = .forceRightToLeft
I'm not able to get control back in my tabcontroller file. TabBar is custom created. I checked with awakefromNib() but it is not getting invocated every time. Is there any tab bar method where I can get control when it appears every time or is there any way to change language of tab bar item titles?

After changing app language in your VC create and call function localizeTabBar():
func localizeTabBar() {
tabBar.items![0].title = "1tab".localized
tabBar.items![1].title = "2tab".localized
tabBar.items![2].title = "3tab".localized
tabBar.items![3].title = "4tab".localized
}
For .localized use this String Extension

Or something like that:
func localizeTabBar() {
tabBar.items?.enumerated().forEach{ (index, item) in
item.title = "\(index)tab".localized
}
}
For .localized use this String Extension

Related

Menu bar icon disappears after window resize

I created the default Cocoa macOS project in Xcode and modified AppDelegate so that it adds a menu bar icon and makes the application window fullscreen. What happens instead is that the window appears normally and the menu bar icon (which should be a T) flashes very quickly in the macOS status bar and disappears. If I comment out the code in applicationDidFinishLaunching, the menu bar icon works. If I remove awakeFromNib and don't create a status item, the application becomes fullscreen. If I try to do both at the same time, it won't work, and I would really like to know what I'm doing wrong. This is the main part of my AppDelegate.swift file:
var item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
override func awakeFromNib() {
let menu = NSMenu(title: "W")
menu.addItem(NSMenuItem(title: "Quit", action: #selector(AppDelegate.quit), keyEquivalent: ""))
item.title = "T"
item.menu = menu
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
let window = NSApplication.shared.windows.first!
window.setFrame(NSScreen.main!.frame, display: true)
}
Turns out
let window = NSApplication.shared.windows.first!
doesn't return the application window, but it seems to return the menu icon. I changed this to
let window = NSApplication.shared.windows[1]
and it worked.

Set all back button titles in UINavigationController

I'm trying to be clever about setting all title properties of the the "Back" buttons in a UINavigationController so that I don't have to do self.navigationController.navigationBar.backButtonItem.title = "Back" everywhere or subclass a UINavigationController and set it everywhere, so I've created this extension:
extension UINavigationItem {
open var backBarButtonItem: UIBarButtonItem? {
get {
return self.backBarButtonItem
}
set {
newValue?.title = "Back"
backBarButtonItem = newValue?
}
}
}
But it says 'backBarButtonItem' used within its own type.
Has anybody done this before or can think of a way to make it work?
You are getting this error because you cannot create a variable with the name which is similar to those variables which are defined in the SDK.
You can't override the existing functionality
Like in your case you are naming it as backBarButtonTitle which is defined as open var backBarButtonItem: UIBarButtonItem? in UINavigationBar class of UIKit
As it is mentioned in doc of Apple
Extensions can add new functionality to a type, but they cannot
override existing functionality.
Please follow this Screen shot Image then run your project . I think you can solved your problem easily :)

UI testing a tab bar controller

I have built a simple tab bar with 3 tabs.
I want to run a UI test to make sure that if the user clicks a tab bar item, the correct view controller shows. How would I go about doing that? Below is the code I would start with, just don't know how to write my assertion.
func testTabBarMyProfileButton() {
let tabBarsQuery = XCUIApplication().tabBars
tabBarsQuery.buttons["My Profile"].tap()
}
func testTabBarGraphsButton() {
let tabBarsQuery = XCUIApplication().tabBars
tabBarsQuery.buttons["Graphs"].tap()
}
func testTabBarAboutButton() {
let tabBarsQuery = XCUIApplication().tabBars
tabBarsQuery.buttons["About"].tap()
}
You can access the tabbar button by its position:
app.tabBars.buttons.element(boundBy: 2).tap()
If you have different controls in each view controller shown on each tab bar, you can make assertions if they exist or not (what is expected).
For example if the first tab bar has UILabel named "First name" you can assert if it exists by writing
Let theLabel = app.staticTexts["myValue"]
XCTAssert(theLabel.exists).to(beTrue)
And on the other screens do the same thing for the different controls.
If anyone finds this looking to UI test the contents of another app, I just found a solution..
The tab bar item is a lazy variable and needs to be touched before you can reference a tab bar button by value. Add this line:
tabBarItem.accessibilityIdentifier = "my-snazzy-identifier"
to the viewDidLoad method and you should be able to do this in your UI tests:
app.tabBars.buttons["Button Title"].tap()
You can test the title of the navigation bar.
XCTAssert(app.navigationBars["Graphs"].exists)
See my GitHub repo for a more detailed UI Testing example.

How to set an accessibilityIdentifier on a UITabBar's buttons

Like the title states, how do you programmatically set the accessibilityIdentifier on a UITabBar's buttons? I'm currently trying to set the UITabBarItem's accessibilityIdentifier before adding it to the tab bar, but that doesn't seem to carry through to the button.
How do you go about setting an accessibilityIdentifier on the actual buttons that are displayed on the page?
When I ran into the same problem, I ended up setting the identifier on the underlying UITabBarButton (not sure if apple would object to that though..)
You need to setup your UITabbar with your UITabBarItem and their identifiers first, then:
//Set the tabbar items and their identifiers first, then, copy them on the underlying UITabBarButton using this code
int index = 0;
for (UIControl *control in controller.tabBar.subviews)
{
if ([control isKindOfClass:UIControl.class] && index < tabBarController.tabBar.items.count)
{
control.accessibilityIdentifier = controller.tabBar.items[index].accessibilityIdentifier;
index++;
}
}
I had this problem with SwiftUI. Putting the accessibilityIdentifier in a different place fixed it:
TabView(selection: $tabSelection) {
// ...
// ...
.tag(0)
.tabItem {
Label("Catalog", systemImage: "list.dash")
.accessibilityIdentifier("tabCatalog") // <-- Works here
}
.accessibilityIdentifier("tabCatalog") // <-- Doesn't work here
}
For UI tests, I'm doing it as follows, in a view controller:
self.tabBarItem.accessibilityIdentifier = #"NewRecordTab";
in Swift that would be
self.tabBarItem.accessibilityIdentifier = "NewRecordTab"
The tabBarItem property is not an inheritor of UIView. It is only used by the UITabBarController class when adding a child tab to it. After the tabBarController has loaded and initialized the tabs, it is useless to change the values of the tabBarItem property. Therefore, it is critically important where exactly you set the value of tabBarItem.accessibilityIdentifier.
The only working way is to set a value in the controller initializer as shown in the example below:
class ViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
self.tabBarItem.accessibilityIdentifier = "myTabIdentifier"
}
}
If you set the value of this property elsewhere (for example, in the viewDidLoad method), then UITabBarController will never know about it, because the viewDidLoad method is called after tabBarController has initialized its tabs.
Alternatively, if the solution above does not suit you, you can interact with tabs using the tab index as follows:
let app = XCUIApplication()
app.tabBars.buttons.element(boundBy: tabIndex).tap()

Creating a floating menu in an iOS application

Looking to create a floating menu in Swift for an iOS application I am developing. Something along the lines of the little red circle menu as shown in the following image.
My initial thoughts were to extend the UIViewController class and add the respective drawing/logic there, however, the application is comprised of a few other controllers, more specifically the UITableViewController which in itself extends UIViewController. Is there perhaps a good place for an extension perhaps? Or is there a more eloquent way of drawing the menu on specific views without the mass duplication of menu related code?
The menu itself will be shown on most screens, so I need to selectively enable it. It'll also be somewhat contextual based on the view/screen the user is currently on.
Any awesome ideas?
You can create your own with the animations and all the things, or you can check this library
https://github.com/lourenco-marinho/ActionButton
var actionButton: ActionButton!
override func viewDidLoad() {
super.viewDidLoad()
let twitterImage = UIImage(named: "twitter_icon.png")!
let plusImage = UIImage(named: "googleplus_icon.png")!
let twitter = ActionButtonItem(title: "Twitter", image: twitterImage)
twitter.action = { item in println("Twitter...") }
let google = ActionButtonItem(title: "Google Plus", image: plusImage)
google.action = { item in println("Google Plus...") }
actionButton = ActionButton(attachedToView: self.view, items: [twitter, google])
actionButton.action = { button in button.toggleMenu() }
}
There is another alternative with this great library :
https://github.com/yoavlt/LiquidFloatingActionButton
You just have to implement the delegate and the dataSource in your ViewController:
let floatingActionButton = LiquidFloatingActionButton(frame: floatingFrame)
floatingActionButton.dataSource = self
floatingActionButton.delegate = self
You could use view controller containment. The menu can be its own view controller with its view laid transparently over top the content view controller.
For example this can be set up in the storyboard by dragging out two container views into a vanilla view controller.

Resources