Is there any way to insert an NSLocalizedString in interface builder.
For example set a label text to a localized string instead of a static string?
I really hate to create a property for every single item that requires a localized string.
This post might have some tips for you:
http://blog.wilshipley.com/2009/10/pimp-my-code-part-17-lost-in.html
Even if this post is old, for those interested in automatically localizing your IB files, check this out: https://github.com/angelolloqui/AGi18n
DISCLAIMER: I am the developer of the library
You can take advanced of the User Defined Runtime Attributes:
http://cupobjc.blogspot.com.es/2014/04/interfaz-builder-localization.html
First define a new category for UILabel:
#import "UILabel+Localized.h"
#implementation UILabel (Localized)
-(void) setTextLocalized:(NSString *)aText{
[self setText:NSLocalizedString(aText, nil)];
}
#end
Then in the interface builder, User Defined Runtime Attributes :
textLocalized String your string to localized
To avoid creating a bunch of categories, create just one that categorize the NSObject and then check for the isKindOfClass as suggested. See the code below:
#import "NSObject+Localized.h"
#implementation NSObject (Localized)
///
/// This method is used to translate strings in .xib files.
/// Using the "User Defined Runtime Attributes" set an entry like:
/// Key Path: textLocalized
/// Type: String
/// Value: {THE TRANSLATION KEY}
///
-(void) setTextLocalized:(NSString *)key
{
if ([self isKindOfClass:[UILabel class]])
{
UILabel *label = (UILabel *)self;
[label setText:NSLocalizedString(key, nil)];
}
else if ([self isKindOfClass:[UIButton class]])
{
UIButton *button = (UIButton *)self;
[button setTitle:NSLocalizedString(key, nil) forState:UIControlStateNormal];
}
else if ([self isKindOfClass:[UIBarButtonItem class]])
{
UIBarButtonItem *button = (UIBarButtonItem *)self;
[button setTitle:NSLocalizedString(key, nil)];
}
}
#end
NSLocalizedString is not the recommended way to localize Interface Builder files. Check out ibtool:
http://www.bdunagan.com/2009/03/15/ibtool-localization-made-easy/
I have done same thing as #OAK mentioned. Here is full code.
Interface Builder Localization HowTo
Related
is there a way to pass a boolean to the addClicked method beside using button.tag for the code below?
[cellview.buttonAdd addTarget:self action:#selector(addClicked:) forControlEvents:UIControlEventTouchUpInside];
-(void) addClicked:(id)sender {
}
THanks in advance.
if you want to add a integer property , you can use tag.
if you want to add a nonInteger property , you must use a category with Associative References,the inheritting UIButton can not post property at all.
you can see this :
Subclass UIButton to add a property
Try something like this:
-(void) addClicked:(id)sender
{
UIButton * button = (UIButton*)sender;
NSLog(#"Button Tag: %i", button.tag);
}
Not sure what you mean by pass Boolean.
Short answer: You cannot pass extra information into the method directly.
Why would you want to do that anyway though? What does the button "know" that it would need to communicate, other than the fact that it was clicked?
The way this should be done is via an instance variable in the class that implements the click handler.
If you really must maintain state inside the button itself, subclass it:
#interface CustomButton : UIButton
#property (nonatomic, assign) BOOL myBoolValue;
#end
/* ... */
- (void)addClicked:(id)sender
{
CustomButton *button = (CustomButton *)sender;
if (button.myBoolValue) {
// Whatever you want to do.
}
}
I am trying to "Connect" my objects in the view to the File's Owner using the interface builder, but the blue line does not 'link' with it. Here is my code:
CalculatorViewController.h:
#import <UIKit/UIKit.h>
#import "CalculatorBrain.h"
#interface CalculatorViewController : UIViewController {
IBOutlet UILabel *display;
IBOutlet UIButton *button;
CalculatorBrain *brain;
BOOL userIsInTheMiddleOfTypingANumber;
}
- (IBAction)digitPressed: (UIButton *)sender;
- (IBAction)operationPressed: (UIButton *)sender;
#end
CalculatorViewController.m:
#import "CalculatorViewController.h"
#implementation CalculatorViewController
- (CalculatorBrain *)brain
{
if (!brain) {
brain = [[CalculatorBrain alloc] init];
}
return brain;
}
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [[sender titleLabel] text];
if (userIsInTheMiddleOfTypingANumber) {
[display setText: [[display text] stringByAppendingString:digit]];
} else {
[display setText:digit];
userIsInTheMiddleOfTypingANumber = YES;
}
}
- (IBAction)operationPressed: (UIButton *)sender
{
if (userIsInTheMiddleOfTypingANumber) {
[[self brain] setOperand: [[display text] doubleValue]];
userIsInTheMiddleOfTypingANumber = NO;
}
NSString *operation = [[sender titleLabel] text];
double result = [[self brain] performOperation:operation];
[display setText: [NSString stringWithFormat: #"%g", result]];
}
#end
Try and check if class of your File's Owner is set to your ViewController Class properly in your XIB.
For checking Go to your XIB
Click on File's Owner and Open the Inspector.
In inspector, go to the last(fourth) tab and check whether you have set your class as <yourViewControllerName>
Hope this helps you.
EDIT:
For better understanding I have added an image of where you need to look for the class.
Also please cross check that you have declared the variables with IBOutlet Prefix in your ViewController's header file
Once you have made sure the File's Owner is set to be CalculatorViewController, make sure that the IBOutlet type in the CalculatorViewController.h file matches the UI component type you are trying to connect it to. Your header defines an IBOutlet for a UILabel, which means only a UILabel can be connected to it.
If the component is of a different type, lets say a UIButton, then you would change your header file to include an IBOutlet like so:
IBOutlet UIButton *button;
Once you have defined the UIButton in your header, switch to IB. Double check that file's owner is set to your viewcontroller class, then add a UIButton to the view. Then you should be able to either:
ctrl+drag from the component to the file's owner
right-click on the file's owner and get a HUD styled popup showing all available IBOutlet's. click and drag from the UIButton one to the UIButton in your view.
If you want the UIButton in IB to trigger one of your defined IBActions, you will make a connection to the action. I usually perform this by right-clicking on the File's Owner, which will show all available IBActions and IBOutlets.
Hope this helps!
Did you set the file owner(in interface builder) to the class CalculatorViewController.
Im no expert on Interface Builder or Objective C, but did you use:
#property (nonatomic, retain) IBOutlet UIButton *yourButton;
and then in the .m file:
#synthesize yourButton;
Im also not sure, but you may have to 'build' it first, then
go into Interface Builder and then it may 'connect' up.
I need to add two additional properties (NSString * and NSMutableArray *) along with three extra methods to UIButton. I also want to reference the new objects using a supertype if that is possible. I do not necessarily want to subclass (as I read that it is tricky and not recommended), but I am quite new to Objective-C and iOS development and don't know what else to do.
I tried to subclass UIButton with my subclass implementing a formal protocol in the following way:
#interface Button : UIButton <MyProtocol> ...
However, I found out this doesn't work like I thought it would, as buttonWithType: returns an object from a subclass. What else can I do to achieve the desired result?
-- EDIT:
Ok, my current code is like this:
#interface Button : UIButton <SteapeObject> {
ActionQueue * actions;
Meta meta;
}
#property (nonatomic, retain) ActionQueue * actions;
#property (nonatomic) Meta meta;
- (id) initWithFrame:(CGRect)frame;
...
And the implementation:
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
NSLog (#"finally");
}
return self;
}
An still doesn't work. It seems that when I invoke:
Button * button = [Button buttonWithType: UIButtonTypeRoundedRect];
NSLog (#"%#", [button description]);
I should get two 'finally' strings and two descriptions in the log. However, I only get the two description strings:
[Session started at 2011-02-24 09:47:14 +0100.]
2011-02-24 09:47:15.431 IphoneClient3[702:207] <UIRoundedRectButton: 0x5f47690; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x5f47240>>
2011-02-24 09:47:15.461 IphoneClient3[702:207] <UIRoundedRectButton: 0x6a0f000; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x6a344b0>>
And you can see that the type is still UIRoundedRectButton, but the buttons do not respond to my added methods. Actually, since my overriden initWithFrame doesn't get called, that is to be expected. Perhaps I should default to implementing a custom control...
As far as I know, the doc does not say subclassing of UIButton is not recommended.
I have done it multiple times to add custom properties — without problems.
The only thing to do is to create the button using:
[Button buttonWithType:UIButtonTypeCustom]; // won't work for other button types though
Use a Category.
#interface UIButton (MyButtonCategory)
- (void) myMethod;
#end
#implementation UIButton (MyButtonCategory)
- (void) myMethod
{
NSLog(#"Called myMethod!");
}
#end
[EDIT]
Alternatively, if I finally understand you, you can do this.
#interface MyButton : UIButton
- (id) initWithFrame:(CGRect)rect;
#end
#implementation MyButton
- (id) initWithFrame:(CGRect)rect
{
if ((self = [super initWithFrame:rect])){
// Do your init in here
}
return self;
}
#end
Then calling
MyButton *btn = [MyButton buttonWithType:UIButtonTypeRoundedRect];
Should get you what you want. buttonWithType should call initWithFrame on your subclass.
I found that it is not possible to accomplish that task with the current implementation of the SDK.
Categories might help. Implement like this:
//In the UIButtonMyExtras.h file
#interface UIButton(MyExtras)
//extras
#end
//In the UIButtonMyExtras.m file
#implementation UIButton(MyExtras)
//extra implementation
#end
This adds these extras to every UIButton in your project.
I need some guidance on creating a UITableViewCell that has an image on the left which can be toggled. The image should be tappable and act as a toggle (checkbox).
My parts I'm struggling with are:
How do I detect taps on the image and handle those differently to didSelectRowAtIndexPath?
How do I change the image without performing a [tableView reloadData]?
It's actually pretty easy.
Just create a new subclass of UIControl and put it all in there (no need for a separate controller.) Let's call it ToggleImageControl.
#interface ToggleImageControl : UIControl
{
BOOL selected;
UIImageView *imageView;
UIImage *normalImage;
UIImage *selectedImage;
}
Create a ToggleImageControl for each cell, and add it at the appropriate position.
ToggleImageControl *toggleControl = [[ToggleImageControl alloc] initWithFrame: <frame>];
toggleControl.tag = indexPath.row; // for reference in notifications.
[cell.contentView addSubview: toggleControl];
Add a UIImageView to contain the image. Add a target for the touch event.
- (void) viewDidLoad
{
normalImage = [UIImage imageNamed: #"normal.png"];
selectedImage = [UIImage imageNamed: #"selected.png"];
imageView = [[UIImageView alloc] initWithImage: normalImage];
// set imageView frame
[self.view addSubview: imageView];
[self addTarget: self action: #selector(toggleImage) forControlEvents: UIControlEventTouchUpInside];
}
Set the UIImageView's image property to update the image; that will trigger the redraw without side-effects.
- (void) toggleImage
{
selected = !selected;
imageView.image = (selected ? selectedImage : normalImage);
// Use NSNotification or other method to notify data model about state change.
// Notification example:
NSDictionary *dict = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: self.tag forKey: #"CellCheckToggled"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"CellCheckToggled" object: self userInfo: dict];
}
You will obviously need to massage some stuff. You probably want to pass in the two image names to make it more reusable, and also I'd recommend specifying the notification name string from outside the object as well (assuming you are using the notification method.)
Here's an implementation of the "override touchesBegan:" approach I'm using that is simple and seems to work well.
Just include this class in your project and create and configure a TouchIconTableViewCell instead of a UITableView cell in your tableView:cellForRowAtIndexPath: method.
TouchIconTableViewCell.h:
#import <UIKit/UIKit.h>
#class TouchIconTableViewCell;
#protocol TouchIconTableViewCellDelegate<NSObject>
#required
- (void)tableViewCellIconTouched:(TouchIconTableViewCell *)cell indexPath:(NSIndexPath *)indexPath;
#end
#interface TouchIconTableViewCell : UITableViewCell {
id<TouchIconTableViewCellDelegate> touchIconDelegate; // note: not retained
NSIndexPath *touchIconIndexPath;
}
#property (nonatomic, assign) id<TouchIconTableViewCellDelegate> touchIconDelegate;
#property (nonatomic, retain) NSIndexPath *touchIconIndexPath;
#end
TouchIconTableViewCell.m:
#import "TouchIconTableViewCell.h"
#implementation TouchIconTableViewCell
#synthesize touchIconDelegate;
#synthesize touchIconIndexPath;
- (void)dealloc {
[touchIconIndexPath release];
[super dealloc];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = [((UITouch *)[touches anyObject]) locationInView:self];
if (CGRectContainsPoint(self.imageView.frame, location)) {
[self.touchIconDelegate tableViewCellIconTouched:self indexPath:self.touchIconIndexPath];
return;
}
[super touchesBegan:touches withEvent:event];
}
#end
Each time you create or re-use the cell, set the touchIconDelegate and touchIconIndexPath properties. When your icon is touched, the delegate will be invoked. Then you can update the icon or whatever.
So the "..obviously need to massage some stuff.." comment means "...this code doesn't work...".
So
- (void) viewDidLoad
should be
- (id)initWithFrame:(CGRect)frame
{
if ( self = [super initWithFrame: frame] ){
normalImage = [UIImage imageNamed: #"toggleImageNormal.png"];
selectedImage = [UIImage imageNamed: #"toggleImageSelected.png"];
imageView = [[UIImageView alloc] initWithImage: normalImage];
// set imageView frame
[self addSubview: imageView];
[self addTarget: self action: #selector(toggleImage) forControlEvents: UIControlEventTouchDown];
}
return self;
}
As - (void) viewDidLoad never gets called.
It seems that Apple uploaded "TableMultiSelect" as sample codes on iOS Developer Program since 2011-10-12.
Multiple selection in edit mode can be enabled by this code.
self.tableView.allowsMultipleSelectionDuringEditing = YES;
http://developer.apple.com/library/ios/#samplecode/TableMultiSelect/Introduction/Intro.html
Though it can be used only from iOS5.
Several hours I could not find this sample code in Stack Overflow, so I added this info to this post.
There's an even EASIER way to do this, if you override touchesBegan: you need to do an if statement to decide if it's within the check marks proximity, if it's not call [super touchesBegan:touches withEvent:event] and it will act as though it was selected.
I do something similar (starring a favorite) like this, but I think you're demanding a lot from the thumbs of iPhone users with the cell directing people to another view. First of all, I would check out the detail disclosure option on cells. One of the options is a pre-made arrow button that you can attach to. Your call.
You might be able to get away with catching the didSelectRowAtIndexPath event and then doing some other logic instead of redirecting if the touch was on your checkbox, although I don't know how you would get the position. This means you might need to find a way to get ahold of the touch event before it calls didSelectRowAtIndex path, which I'm not quite sure how to do. Have you worked with handling touchesBegan and the like yet?
When an IBAction is called:
-(IBAction) onClick1: (id) sender;
What is passed in the sender? Since it's hooked up through the IB, I'm not really sure. My question is how to get the text of the button to be the passed object (NSString most likely) so that I could call it inside the action implementation.
-(IBAction) onClick1: (id) sender {
NSLog(#"User clicked %#", sender);
// Do something here with the variable 'sender'
}
The sender should be the control which initiated the action. However, you should not assume its type and should instead leave it defined as an id. Instead, check for the object's class in the actual method as follows:
- (IBAction)onClick1:(id)sender {
// Make sure it's a UIButton
if (![sender isKindOfClass:[UIButton class]])
return;
NSString *title = [(UIButton *)sender currentTitle];
}
It's actually:
-(IBAction) onClick1: (id) sender {
NSLog(#"User clicked %#", sender);
// Do something here with the variable 'sender'
}
sender is not a NSString, it's of type id. It's just the control that sent the event. So if your method is trigged on a button click, the UIButton object that was clicked will be sent. You can access all of the standard UIButton methods and properties programmatically.
-(IBAction)onClick:(id) sender {
UIButton *btn = (UIButton *)sender;
//now btn is the same object. And to get title directly
NSLog(#"Clicked button: %#",[[btn titleLabel] text]);
}
Simply write the following code
-(IBAction) getButtonTitle:(id)sender
{
UIButton *button = (UIButton *)sender;
NSString *buttonTitle = button.currentTitle;
NSLog(#"Button Title %#",buttonTitle);
}
Thats it... you have done!!!
Sender should be defined as type id, not int or NSString. The sender is the actual object that's calling the method; if you hooked it up to a button, it will be a UIButton, if it's a text field, a UITextField. You can use this to get information from the control (for example the text field's current string value), or compare it to an IBOutlet instance variable if you have multiple controls hooked up to the same action method.
You can just use the following to get the button label and determine which one was clicked:
NSLog(#"Clicked button: %#",[[sender titleLabel] text]);
To answer your question, the id is the object from the IB.
To fetch the text from the button:
NSLog(#"Date::%#",[btn titleForState:UIControlStateNormal]);