I'm pretty new to iOS development and I was asking my self if it is possible to use localized strings from my "Localizable.strings" file directly into the storyboard.
For example in Android you can do it from the XML file like this:
android:text="#string/notConnected"
I understood that you can make a localized version of the storyboard, but having different strings files and different storyboards looks pretty ugly to me.
So is it possible to have only strings files and use what I need into the storyboard? Preferably without setting it from code?
EDIT:
This is practically what I want to do:
So is this possible? Is there a legit way to call a string from there like in Android?
I think being able to localise Strings in the storyboard is of significant advantage. I don't agree with #elk_cloner that hooking up IBOutlets for every UILabel is the way forward.
One way of getting it to work is using an #IBInspectable property on a UILabel subclass:
class LocalisableLabel: UILabel {
#IBInspectable var localisedKey: String? {
didSet {
guard let key = localisedKey else { return }
text = NSLocalizedString(key, comment: "")
}
}
}
In the storyboard set the custom class:
In the attributes inspector the localisedKey field will appear and you can just add your key here.
That's it!
EDIT:
You can localise UIButtons the same way, BUT if the text in the storyboard's title field differs from the localised String (which it will in other languages) the setting of the title will animate.
To fix this, put the setTitle in a performWithoutAnimation block:
class LocalisableButton: UIButton {
#IBInspectable var localisedKey: String? {
didSet {
guard let key = localisedKey else { return }
UIView.performWithoutAnimation {
setTitle(key.localized, for: .normal)
layoutIfNeeded()
}
}
}
}
In addition to Leon's answer, you can get rid of the need for an explicit subclass by using an extension:
extension UILabel {
#IBInspectable var localizableText: String? {
get { return text }
set(value) { text = NSLocalizedString(value!, comment: "") }
}
}
According to your requirement it's not possible but
You don't need different storyboards for localization
Suppose you want to localize a label string.
Draw and outlet and change text using
mylabel.text = nsLocalizedString("THIS_IS_MY_STRING",nil);
Of course in your localization file there will be a line.You must have different files for different language.Suppose you have a file for english and there must be a line.
"THIS_IS_MY_STRING" = "This is my string";
When you compile your app, that function will use mapping to localize your app.
Edit:
If you want detail information please have a look at these tutorials
internationalization-tutorial-for-ios-2014
and ios-localization-tutorial
There are some online script(e.g localize.py) which will help you to automatically search all of your code and find out nslocalizedString function and make lines in your localizableString files. like this.
"THIS_IS_MY_STRING" = "THIS_IS_MY_STRING"
and later on you just have to write actual string there. :)
Make a button class and set #IBInspectable attribute to the button class
class Button: UIButton {
#IBInspectable public var referenceText: String = "" {
didSet {
self.setTitle(NSLocalizedString(referenceText, comment: ""), for: .normal)
}
}
}
Then in the storyboard you can set referenceText
The button text will be "Sign In"
Another possible way is to localize the storyboard and simply change the values for labels and buttons directly on the .string file.
First select the storyboard, and click on localize:
Then you'll be able to select the languages you want.
This way you'll be able to continue developing on your language of choice and simply edit the .string file that is generated
for button
extension UIButton {
#IBInspectable var localizableText: String? {
get { return titleLabel?.text }
set(value)
{
setTitle(NSLocalizedString(value!, comment: ""), for: .normal)
}}}
Related
We have updated out Swift 2.3 project to Swift 3 recently using Xcode 8.2.1 (8C1002), and now most of our UI Tests related with tableViews and the isSelected property aren't working. It's always returning false, even when the object is selected (we can see it in the iOS Simulator).
Has anyone experienced similar issues? Our code used to work normally in Swift 2.3 before the conversion. Here is how we retrieve a tableView cell:
let cell = app.tables.cells.element(at: 4)
Note: app is a XCUIApplication.
And here is how we check if it's selected or not:
XCTAssert(cell.isSelected)
Another observation is that we are sure that the object exists because waitForExpectations is returning true:
let existsPredicate = NSPredicate(format: "exists = 1")
expectation(for: existsPredicate, evaluatedWith: cell, handler: nil)
waitForExpectations(timeout: 20, handler: nil)
EDIT: In order to replace isSelected, I've tried to use NSPredicate with selected = 1 and with isSelected = 1. None worked. I also tried to use acessibilityValue based in other question's answer, however it wasn't that simple since sometimes the items in my table view are selected/unselected programatically. Also, that method involved adding test code to the app, which isn't a good practice.
EDIT AFTER BOUNTY END: Since no one could find a solution for that problem and that's obviously a bug in Xcode, I've submitted a bug report to Apple. I will comment here when they release an Xcode version with the fix.
EXTRA EDIT: One day after my last edit, dzoanb came with a functional answer.
I made a few tests and a little research. You can check out the app created for this purpose >>here<<. It would be great if you could check it out (it required a little bit of work). There are also UI tests to prove it works. Also, two options are available, one is vanilla XCTest and one library with a lot of helpers I'm creating with my colleagues AutoMate. But that's not the point.
Here is what I found out:
1) isSelected property of XCUIElement depends on accessibilityTrait. Element to be selected in XCTest has to have UIAccessibilityTraitSelected set.
2) I couldn't reproduce Your problem but I was able to control isSelected property.
3) Yes, it requires a little bit of code, but should work well with VoiceOver if it is important for You.
All necessary code is in Your custom UITableViewCell subclass. And uses overriding UIAccessibilityElement accessibilityTraits property.
private var traits: UIAccessibilityTraits = UIAccessibilityTraitNone
// MARK: UITableViewCell life cycle
override func awakeFromNib() {
super.awakeFromNib()
traits = super.accessibilityTraits
}
// MARK: UIAccessibilityElement
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isSelected {
return traits | UIAccessibilityTraitSelected
}
return traits
}
set {
traits = newValue
}
}
Hope it helps.
Couldn't get that code to compile under Swift 4.
This worked for me.
public override var accessibilityTraits: UIAccessibilityTraits {
get {
if isSelected {
return super.accessibilityTraits.union(.selected)
}
return super.accessibilityTraits
}
set {
super.accessibilityTraits = newValue
}
}
Have you tried making a break point before and after the tap, and check the value of the cell? Like the WWDC video here: https://youtu.be/7zMGf-0OnoU
(See from 10 minutes in)
isSelected only works on views which inherit from UIControl. UIControl.isSelected informs XCUIElement.isSelected.
Since UITableViewCell does not inherit from UIControl, you aren't seeing the value you want in your tests when you observe cell.isSelected.
I suggest that if you want this to be testable via UI tests that you file a feature request with Apple to make UIControl a protocol, which you could then extend your cells to conform to, or add UITableViewCell.isSelected to the properties that inform XCUIElement.isSelected.
#dzoanb solution can work without adding a private var:
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isSelected {
return super.accessibilityTraits | UIAccessibilityTraitSelected
}
return super.accessibilityTraits
}
set {
super.accessibilityTraits = newValue
}
}
In order to add VoiceOver support for my app(using Interface Builder),I set a button's "Accessibility Identity -> Description" to "Mute" like this.This actually set accessibility label.
And now,I want to add localization for this button,including accessibility label of it.
How can I do that?
ps: I've tried the programmatically way(NSLocalizedString) and creating xib file for each language.But both of them are not good for maintenance.I want to know if I can localize it in ".strings" way
You can create extension like this and then set keys in UI Builder but handle localization in .strings file
#IBDesignable
public extension UIView {
#IBInspectable
var accessibilityLabelKey: String {
get { return "" }
set {
self.accessibilityLabel = NSLocalizedString(newValue, comment:newValue)
}
}
}
i just want to modify the size of character of the UITextView. until now is possible to attach the string, but i Try to change the dimension: is not possible.
As i searched into the Forum i found that some people got it selecting Editable and deselecting it. Other people got it by selecting Selectable from the View properties. Then i tried this way... no way to change. Only Plain text.
import UIKit
#objc(TextControllerSwift) class TextControllerSwift: UIViewController {
var selectedMovie: String?
var textPlaying: String?
#IBOutlet weak var textMuseum: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
realPlay()
}
func playText(textSelect: String) {
textPlaying = textSelect
}
//Giving the time to Segue method to wakeup.
func realPlay(){
var textRoom: String?
//reading the file .txt
let path = NSBundle.mainBundle().pathForResource(textPlaying, ofType:"txt")
if (path != nil){
do {
textRoom= try String(contentsOfFile: path!, encoding: NSUTF8StringEncoding)
//here i'm getting the string.
}
catch {/* ignore it */}
//i tried it these options...
textMuseum.editable=true
textMuseum.selectable=true
textMuseum!.text=""
//Got the text, now i put into UiView
textMuseum.font = UIFont(name: "Arial-Unicode", size: 50)
textMuseum.text=textMuseum.text.stringByAppendingString(String(textRoom!))
}}}
hmmm where am i getting wrong?
because i changed the textMuseum font. Should i free some Costraint on the UITextView Object put in the StoryBoard? also with the editable and selectable removed the result is the same. why?
thank you for every help.
EDIT:
Added Git Repository - No Working Video as i deleted this part. To see the problem just click on uisegmented "testo" and select play or the table.
https://github.com/sanxius/TryText.git
After reviewing your source code:
Make UITextView selectable: textMuseum.selectable = true
If you want to use custom font then do not forget it's file name to add it to Info.plist Fonts provided by application array.
Use existing font name. There is no font with name Arial-Unicode. Arial-Unicode.ttf is file name not font name.
You can find your font name by listing all loaded fonts with:
for familyName in UIFont.familyNames() {
for fontName in UIFont.fontNamesForFamilyName(familyName) {
print(fontName)
}
}
Also iOS has built-in Arial font that can be loaded by UIFont(name: "ArialMT", size: 50). So you do not need to add your Arial-Unicode.ttf.
Is it possible to get the placeholder value of UITextField, which is designed in storyboard?
I need to validate the form which is having more textfields, so instead of giving static value, I want to give placeholder string of particular textfield as alert message.
Use the placeholder property:
textField.placeholder
For placeholder first you should create IBOUTLET for textfield and then do:
if let str = textfiled.placeholder {
if !str.isEmpty {
//do something
}
}
The UITextField class has a placeholder property. To reference the UITextField in code, you'll need to create an outlet for it in your view controller.
If you named the outlet myTextField, you could reference the placeholder like this:
let placeholder = self.myTextField.placeholder
Its work ...
For getting placeholder text you need to wire up your textfield with its IBOutlet object like,
#IBOutlet var txtField : UITextField?
Now you set Placeholder Statically or dynamically
self.txtField!.attributedPlaceholder = NSAttributedString(string:"Good", attributes:[NSForegroundColorAttributeName: UIColor.grayColor()])
After that anywhere you can simply get its placeholder text and compare or validate ur textfield like
if((self.txtField!.placeholder! as NSString).isEqualToString("Good"))
{
// Do here Whatever you want
}
else if(self.txtField!.placeholder!.isEmpty == true)
{
// check its empty or not
}
Simply write like below code,
if((self.myTextFiled!.placeholder! as NSString).isEqualToString("AnyString"))
{
// your code here
}
I'm implementing a favourite button in swift for my quote application. Since I want to disable the user to be able to favourite a quote twice. I must change the text to unlike and then compare the button text mode being unlike to the button text mode being like. And then do furthermore things based on these conditions. It would look something like:
#IBAction func favour(sender: AnyObject) {
if liketext.text == "Like"{
liketext.setTitle("Unlike", forState: UIControlState.Normal)
makeQuoteFavourite()
} else if liketext.text == "Unlike" {
liketext.setTitle("Like", forState: UIControlState.Normal)
}
}
However as many of you know, a button outlet cannot have the .text function. #IBOutlet var liketext: UIButton! How would I be able to compare button strings to normal strings? Are there other possible solutions?
UIButtons simply work differently than UILabels. They have control states for one thing. And their (title) text can be set on a per-control-state basis.
So you have to use the currentTitle property:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIButton_Class/#//apple_ref/occ/instp/UIButton/currentTitle
or the titleForState method:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIButton_Class/#//apple_ref/occ/instm/UIButton/titleForState:
Now those titles are normal String objects; it's the name of the property that's different.