Hi im making an app with uikit and i want to set a profile image(received from url) on Tabbar item. I am expecting the updated profile image on TabBarItem. Also i want to add a border around the image when the tab is selected.
I found this solution, but the image does not appear in the tabitem.
Set user's profile picture on Tabbar item
extension UITabBarController {
func addSubviewToLastTabItem(_ image: UIImage) {
if let lastTabBarButton = self.tabBar.subviews.last, let tabItemImageView = lastTabBarButton.subviews.first {
if let accountTabBarItem = self.tabBar.items?.last {
accountTabBarItem.selectedImage = nil
accountTabBarItem.image = nil
}
let imgView = UIImageView()
imgView.frame = tabItemImageView.frame
imgView.layer.cornerRadius = tabItemImageView.frame.height/2
imgView.layer.masksToBounds = true
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
imgView.image = image
self.tabBar.subviews.last?.addSubview(imgView)
}
}
}
Could someone help me to do that?
Did you add the second part of code:
let yourImage = UIImage(named: "your-image")
override func viewDidLayoutSubviews() { // add to any vc
super.viewDidLayoutSubviews()
tabBarController?.addSubviewToLastTabItem(yourImage)
}
When you call addSubviewToLastTabItem("String") it is critical print correctly parameter without mistakes.
Could you post this part of code?
One more thing viewDidLayoutSubviews will work if you rotate iPhone, double check please that you are calling addSubviewToLastTabItem() in viewDidLoad()
I've created a customized keyboard using UIView. I'm trying to auto-fill the letters using a for loop but with no success.
func initializeSubviews() {
let xibFileName = "keyboardView" // xib extension not included b
let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)![0] as! UIView
self.addSubview(view)
view.frame = self.bounds
setKeyboardText()
}
#IBOutlet var keyboardLetters: [myKeyboardBtn]!
func setKeyboardText() {
let str = "abcdefghijklmnopqrstuvwxyz"
let characterArray = Array(str)
for (Index, key) in keyboardLetters.enumerated() {
key.titleLabel?.text = String(characterArray[Index])
}
// [a,b,c,d,...]
}
what am I doing wrong?
According to Apple
"To set the actual text of the label, use setTitle(_:for:)
(button.titleLabel.text does not let you set the text)."
Im more familiar with ActionScript3 and see many similarities in Swift2, kind of why i am trying out basic coding in Swift2 and Xcode.
Here's my example:
#IBOutlet weak var b1CurrSpeed: NSTextField!
I want to store b1CurrSpeed as a string so i could access the actual textfield component to set its default value when application is loaded.
I'm aiming for Swift2 for osx apps.
Here is a fictional example, not related to any actual code:
var tf:NSTextField = this.getItem("b1CurrSpeed");
tf.stringValue = "Hello world";
Reason to this approach is following...
I would like to store textfield value in NSUserDefaults, the key for defaults would be name of that textfield. So when looping thru the defaults, i would like to get key as string and when ive got that i'd have access to actual component to set its stringvalue property.
Tho, is that good approach in Swift / xCode ?
If you want to create a function for it, do someting like this:
func getStringForKey(key: String) -> String {
guard let result = NSUserDefaults.standardUserDefaults().objectForKey(key) as! String else { return "" }
return result
}
You can set the TextFields value with myTextField.text
Swift's Mirror type can get you close to it but it is limited to NSObject subclasses, can only access stored properties and is read-only.
Yet, there are ways around these limitations if your requirements will allow.
For example, here's an extension that will save and restore defaults values for all UITextfields on a view controller using the name you gave to each IBOutlet.
extension UIViewController
{
func loadDefaults(userDefaults: NSUserDefaults)
{
for prop in Mirror(reflecting:self).children
{
// add variants for each type/property you want to support
if let field = prop.value as? UITextField,
let name = prop.label
{
if let defaultValue = userDefaults.objectForKey(name) as? String
{ field.text = defaultValue }
}
}
}
func saveDefaults(userDefaults: NSUserDefaults)
{
for prop in Mirror(reflecting:self).children
{
if let field = prop.value as? UITextField,
let name = prop.label
{
if let currentValue = field.text
{ userDefaults.setObject(currentValue, forKey: name) }
}
}
}
}
var TestYou = "www,aa.com"
if let url = TestYou as? String {img.hidden = true}else{img.hidden = false}
I want to hide an image if an url is found. If it is not I want to show the image.
What do I have to do to fix it?
If I got that right from your "question" you are searching for something like this (do this in your AppDelegate):
if let url = NSURL(string: ("www.aa.com")) {
// check if your application can open the NSURL
if UIApplication.sharedApplication().canOpenURL(url){
img.hidden = true //hide image
} else {
img.hidden = false //show image
}
}
The officially recommended method for XIB/Storyboard localization is to create .xib and .storyboard files inside xx.lproj (where xx is the two letter language ID) for each localization you want to support.
This creates a problem because you have multiple files that in many cases share the same UI, that are prone to change. If you wanted to re-design the UI for one view, you'll have to do it multiple times (worse if you entered the localizable string values in the xib itself). This goes against the DRY principle.
It seems way more efficient to call NSLocalizedString() where you need it, and just use one XIB or Storyboard for one base localization.
So, why should(n't) I create localized XIB/Storyboard files?
You can make a category on UILabel, UIButton etc. like this:
#import "UILabel+Localization.h"
#implementation UILabel (Localization)
- (void)setLocalizeKey:(NSString*)key
{
self.text = NSLocalizedString(key, nil);
}
#end
and after that on your xib file use User Defined Runtime Attributes to link the UILabel (or UIButton etc.) to a key saved in your Localizable.strings file
This way you can have all your strings in one file and you do not have to create a separate xib for each language.
For just changing text labels I did something like this
+(void) replaceTextWithLocalizedTextInSubviewsForView:(UIView*)view
{
for (UIView* v in view.subviews)
{
if (v.subviews.count > 0)
{
[self replaceTextWithLocalizedTextInSubviewsForView:v];
}
if ([v isKindOfClass:[UILabel class]])
{
UILabel* l = (UILabel*)v;
l.text = NSLocalizedString(l.text, nil);
[l sizeToFit];
}
if ([v isKindOfClass:[UIButton class]])
{
UIButton* b = (UIButton*)v;
[b setTitle:NSLocalizedString(b.titleLabel.text, nil) forState:UIControlStateNormal];
}
}
}
call this function in your viewDidLoad: like this:
[[self class] replaceTextWithLocalizedTextInSubviewsForView:self.view];
It saved me a lot of work declaring and connecting IBOutlets when all you want is localized labels.
Flax's solution works well, one thing to note is that if you have UILabels or UIButtons which are are contained in UICollectionViewCells in UICollectionViews (or similar) and these collections change frequently in the current view, eg due to user action or being populated by an asynchronous request, then to keep the labels updated with the correct localization strings you can call the localization function in viewDidLayoutSubviews instead of viewDidLoad (which is called only once):
- (void)viewDidLayoutSubviews
{
[LocalizationHelper replaceTextWithLocalizedTextInSubviewsForView:self.view];
}
As can be seen from this code, I keep the localization method in a static helper class (as the other chap suggested):
#implementation LocalizationHelper
+(void) replaceTextWithLocalizedTextInSubviewsForView:(UIView*)view
{
for (UIView* v in view.subviews)
... <code from above> ...
}
#end
Would have added this as a comment to the above solution, but I ain't got no 'rep!
As explained by Leszek S you can create a category.
Here I'll give you an example in swift 3 with extension for UILabel and UIButton:
First of all create a "StringExtension.swift" file
Add on it this code:
extension String {
func localized() -> String {
let bundle = Bundle.main
return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")
}
}
Then create another new file with the name you want (for example) "LocalizableObjectsExtensions.swift"
Add on it an extension for UIButton and one for UILabel like this (of course you can create extension for what you want, UITextField...):
extension UIButton {
var localizedText: String {
set (key) { setTitle(key.localized(), for: .normal) }
get { return titleLabel!.text! }
}
}
extension UILabel {
var localizedText: String {
set (key) { text = key.localized() }
get { return text! }
}
}
Now go in your Storyboard and for your button and/or you label that you want localize just add in the identity inspector of you object this:
FYI: here Key Path it's the name of the function you added in your extensions (UIlabel and UIButton) and Value is the name of the key that you want translate automatically which is in your Localizable.strings file. For example in your Localizable.strings (French) you have the key/value "ourOffers" = "NOS OFFRES";
Now build & Run. Your Object will be translated in the language of your device if you have the key/value in your Localizable.string. Enjoy :)
you can automate a lot of it with ibtool. this is a decent introduction: http://www.bdunagan.com/2009/03/15/ibtool-localization-made-easy/
Every place I look says that you have to replicate the entire xib file for each localization instance, even though you really only wanted to rip the text out and replicate the text in a different language for each localization instance.
If anyone knows of a method to replicate only the user visible text of an xib (in a different language) without replicating the entire xib file for each language, please let us know.
Useful post, much easier than multiple XIBs. I extended the code to handle UISegmentedControl:
if ([v isKindOfClass:[UISegmentedControl class]]) {
UISegmentedControl* s = (UISegmentedControl*)v;
for (int i = 0; i < s.numberOfSegments; i++) {
[s setTitle:NSLocalizedString([s titleForSegmentAtIndex:i],nil) forSegmentAtIndex:i];
}
}
I was looking for the exactly answer given by Flax, marked as right, but I needed it in Swift. So I translated into it. Thanks Flax.
func replaceTextWithLocalizedTextInSubviewsForView(view: UIView) {
for v in view.subviews {
if v.subviews.count > 0 {
self.replaceTextWithLocalizedTextInSubviewsForView(v)
}
if (v.isKindOfClass(UILabel)) {
let myLabel: UILabel = v as! UILabel
myLabel.text = NSLocalizedString(myLabel.text!, comment: "Text to translate.")
myLabel.sizeToFit()
}
if (v.isKindOfClass(UIButton)) {
let myButton: UIButton = v as! UIButton
myButton.setTitle(NSLocalizedString((myButton.titleLabel?.text)!, comment: "Text to translate.") as String, forState: .Normal)
myButton.sizeToFit()
}
}
}
That works for Swift 2.1
I used a similar approach as Leszek Szary described for my views in Swift.
Using a Boolean value as opposed to the localization keys, I added an "On/Off" drop down menu that determines whether the initial text values should be localized or not. This allows for the Storyboard to be kept clean without any extra upkeep.
When a value is selected, a single Runtime Attribute is added to the view and is used as a condition from within it's setter.
Here is the code from my .swift file which extends UIButton, UILabel, UITabBarItem and UITextField, including the text field placeholder and button control states:
import UIKit
extension String {
public var localize: String {
return NSLocalizedString(self, comment: "")
}
}
extension UIButton {
#IBInspectable public var Localize: Bool {
get { return false }
set { if (newValue) {
setTitle( title(for:.normal)?.localize, for:.normal)
setTitle( title(for:.highlighted)?.localize, for:.highlighted)
setTitle( title(for:.selected)?.localize, for:.selected)
setTitle( title(for:.disabled)?.localize, for:.disabled)
}}
}
}
extension UILabel {
#IBInspectable public var Localize: Bool {
get { return false }
set { if (newValue) { text = text?.localize }}
}
}
extension UITabBarItem {
#IBInspectable public var Localize: Bool {
get { return false }
set { if (newValue) { title = title?.localize }}
}
}
extension UITextField {
#IBInspectable public var Localize: Bool {
get { return false }
set { if (newValue) {
placeholder = placeholder?.localize
text = text?.localize
}}
}
}
You could also use the new property to easily translate values that are set while your program is running like this:
let button = UIButton()
button.setTitle("Normal Text", for: .normal)
button.setTitle("Selected Text", for: .selected)
button.Localize = true
I came across this post and several others while trying to make xib localization easier for myself. I posted my method of including IBOutles for labels/buttons on this question, worked great for me, keeps all changes limited to the Localization.strings files.
https://stackoverflow.com/a/15485572/1449834
IMHO Xcode has one among the worst localization features available around...
I really don't like developing for Android but I must admit Android Studio has a better localization system.
That said, because I really cannot stand anymore to recreate Storyboard.strings after each mod (you know, Xcode won't update them for you...), this is how I do :
I have a couple of extensions to loop subviews (and subviews of subviews) and I deal with each of the main objects (labels, textfield, buttons...) by localizing their main properties (text, placeholde...) through a simple helper (AltoUtil.ls) which is a "short" version for NSLocalizedString.
Then I insert texts and placeholders with underscores (for example "_first_name", "_email_address") in my storyboard/xibs and I add those strings to each Localizable.strings file.
Now I just need to call the localize() function in viewDidLoad (or whereber I need it) so that I can have the whole view controller localized. For cells I just call the localize() inside the awakeFromNib() method for example.
I'm sure this is not the fastest method (due to subviews loop) but I don't get any slowdown compared to other methods I used to use and it's pretty productive.
import UIKit
extension UIView {
func localize()
{
for view in self.allSubviews()
{
if let label = view as? UILabel
{
label.text = AltoUtil.ls(label.text)
}
else if let textField = view as? UITextField
{
textField.text = AltoUtil.ls(textField.text)
textField.placeholder = AltoUtil.ls(textField.placeholder)
}
else if let button = view as? UIButton
{
button.setTitle(AltoUtil.ls(button.title(for: UIControl.State.normal)), for: UIControl.State.normal)
}
else if let searchBar = view as? UISearchBar
{
searchBar.placeholder = AltoUtil.ls(searchBar.placeholder)
}
}
}
func allSubviews() -> [UIView]
{
return subviews + subviews.flatMap { $0.allSubviews() }
}
}
The second extension is needed to localize view controllers title and tab bar items in view controllers. You can add any item you need to localize.
import UIKit
extension UIViewController {
func localize()
{
self.title = AltoUtil.ls(self.navigationItem.title)
self.tabBarItem?.title = AltoUtil.ls(self.tabBarItem?.title)
self.view.localize()
}
}