iOS Localizable.strings file for XIB files? - objective-c

I'm translating an iOS project into Portuguese and I've created a pt.lproj/Localizable.strings file, and I've added NSLocalizedString() into the source code. It all works! With one slight problem - the strings within XIB files don't get translated... I know this is by design though.
I've seen that the ibtool command can be used to rip strings from an XIB file called x and put it into a file called x.strings... but my question is, is there a way to pull the strings from ALL the xib files and put them all into one .strings file (e.g. Localizable.strings? or even another one called XIBs.strings would be fine?)

You have two options how to translate xib files. One is you connect the UI elements to outlets and set your strings in your viewDidLoad method using the NSLocalizedString macros.
The second option is to provide a separate xib for each language your app supports. You don't have to create them manually, you can use the ibtool command (i assume your source language is English and target is Portugese):
ibtool --strings-file pt.lproj/Example.strings en.lproj/Example.xib –write pt.lproj/Example.xib
To collect strings found in your project you can use genstrings command - however i recommend using this python script to collect all your strings - it can nicely handle the situation when you need to add/remove strings to your app without having to translate and/or manually merge all previous strings
Edit
Oh and i found the article that i learned this trick from

I made a category to do this:
#implementation UIView (Localise)
- (void)localise
{
[self.subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
[view localise];
if ([view isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)view;
[label setText:NSLocalizedString(label.text, nil)];
}
}];
}
Then in your views:
- (void)awakeFromNib
{
[super awakeFromNib];
[self localise];
}
You'll need to add support for other UI elements (textField.placeholder = NSLocal...blah blah), but that'll do the trick for UILabel and children.
Probably not great performance-wise but does the job.

It's going to depend on the app and the number of localizations, but in general I prefer separate .xib files, because layout may change. Put the localized files in pt.lproj/.

Related

How do I re-define size/origin of app window in code, overriding nib/xib file parameters, with Obj-C, Xcode 11.3.1 on Mac OS X 10.15.2 (Catalina)?

I'll try to keep it short. I want to create a 3D FPS game, just for myself, that can run on multiple platforms, but I figured that to keep it simple, perhaps it is best to start off with something that is exclusively for macOS. I opted for Objective-C because
(a) Window Application projects in Xcode can only be coded either in Obj-C or Swift (since we are dealing with Cocoa API) and
(b) Obj-C is closer to old-school then Swift.
But before I learn to draw/render 2D-shapes on the window's canvas by writing code, I have to learn to invoke an application window with its properties set to my liking. I've spent hours doing research and experimenting with chunks of code. This is what I've tried: I open with
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
Then I go with ...
1)
NSWindow *window = [[[NSApplication sharedApplication] windows] firstObject];
NSRect frame = [window frame];
frame.origin.x = 100;
frame.origin.y = 200;
frame.size.width = 100;
frame.size.height = 500;
[window setFrame: frame display: YES];
... and close with ...
NSApplicationMain(argc, argv); // runs the win display function.
}
return (0) ;
}
But no visible changes. Nothing really gets reset. So instead of (1) I tried ...
2)
NSWindow *window = [[[NSApplication sharedApplication] windows] firstObject];
NSPoint newOrigin;
newOrigin.x = 400;
newOrigin.y = 100;
[window setFrameOrigin : newOrigin];
Still nothing. Then instead of (2) I tried:
3)
NSWindowController* controller = [[NSWindowController alloc]
initWithWindowNibName:#"MainMenu"];
[controller showWindow:nil];
Great. Now it's spitting out something I don't understand, especially since I'm new to Obj-C:
2020-02-08 21:53:49.782197-0800
tryout_macApp2[14333:939233] [Nib Loading] Failed
to connect (delegate) outlet from
(NSWindowController) to (AppDelegate): missing
setter or instance variable
I remember dicing around with an ApplicationDelegate, with CGSizeMake(), etc., but it just made the experience really inundating and frustrating. Nothing happened. Then there are NSView, NSViewController, and other classes, which is really mindboggling and begs the question: why are there so many classes when all I want to do is override the preset origin of the window and the dimensions preset by the MainMenu.xib file? (By the way, this project is derived from a Window Application project provided by Xcode.)
I really can't think of anything else to add to give you the entire picture of my predicament, so if you feel that something is missing, please chime in.
[Edit:] Moving forward to phase 2 of my project here: How do I paint/draw/render a dot or color a pixel on the canvas of my window with only a few lines in Obj-C on Mac OS X using Xcode?.
The short answer is that main() is too early to be trying to do this. Instead, implement -applicationDidFinishLaunching: on your app delegate class, and do it there. Leave main() as it was originally created by Xcode's template.
After that, I would say to obtain the window (if there's only going to be one main one), it's better to add an outlet to your app delegate and then, in the NIB, connect that outlet to the window. Then, you can use that outlet whenever you want to refer to the window.
Also, make sure that Visible at Launch is disabled for the window in the NIB. That's so you configure it as you want before showing it.
For a more complex app, it's probably better to not put a window into the Main Menu NIB. Instead, make a separate NIB for the window. Then, load it using a window controller object and ask that for its window.
I love Objective-C but also feel your pain, it has this testy ability to frustrate you endlessly.
I have not really developed a game but let me try and point you in the right direction. I think you need a UIViewController.
Now each UIViewController has a built in UIView that sort of represents the visible portion of it. You can use this or add a UIView and use that, whichever depends on your implementation. For now I'd suggest add a separate UIView and use that rather. Once you're comfortable you can then move the implementation to the UIViewController's view if you need to.
Anyhow, for now, create a UIView subclass, say MyGame or something, as for now all your code will end up there.
To do all of the above is not easy, especially if its the first time. If you can follow some tutorial it will be great. Even if the tutorial just adds a button, you can use it and replace the button with your view.
Anyhow, now that you've got that running and the view you've added shows up in green or some other neon colour just to verify that you can indeed change its properties, you're good to go.
Now you start. In MyGame, implement the
-(void)drawRect:(CGRect)rect
message, grab the context through
UIGraphicsGetCurrentContext
and start drawing lines and stuff on it, basically the stuff I understand you are interested in doing. You can also, through the same context, change the origin of what you are doing.
Hope this helps.

iOS GridView with Sections

I am trying to implement my own gridview similar to KKGridView.
My gridview has extra complexity because it needs to support multiple sections with a varying amount of items / cells in each section. (See below)
It also needs to be able to handle moving one cell from section 1 to section 0 and section 0 to section 1.
I have written the UIView to display the grid but it does not support moving cells from one section to another. The question is does anybody have a gridview that supports this functionality or can advise me of the best way forward as this gridview needs to be supported in iOS 5 & iOS 6
I would try using a combination of UICollectionView, with a UICollectionViewFlowLayout along with PSTCollectionView for iOS5 compatibility.
Take a look at the UICollectionViewDelegateFlowLayout docs, I think you'll find everything you need to build a per-section custom layout as you need.
PSTCollectionView is a "open Source, 100% API compatible replacement of UICollectionView for iOS4.3+". Further, you can use UICollectionView on iOS6 and only fall back to PSTCollectionView on iOS5 devices. Take a look at the GitHub project page.
I'm using this approach in one of the apps I'm working on, and it is really simple to implement.
EDIT: The steps you'd take to make what you want would be roughly as following:
You need to download at include the PSTCollectionView in your project. For that follow the steps on the GitHub page.
In your .h file, import PSTCollectionView.h and add the following two protocols: <PSUICollectionViewDataSource, PSUICollectionViewDelegate>. Note that with this you'll use Apple's UICollectionView in iOS6 and PSTCollectionView in iOS5. Here you should also add a property for your collection view: #property (nonatomic, strong) PSUICollectionView *collectionView;
In the viewDidLoad: method, you need to have something like this:
Code:
PSUICollectionViewFlowLayout *flowLayout = [[PSUICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[flowLayout setItemSize:CGSizeMake(91, 119)];
[flowLayout setMinimumLineSpacing:0];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
And you'd need to implement the following methods to your liking:
Methods:
numberOfSectionsInCollectionView:
collectionView:numberOfItemsInSection:
collectionView:cellForItemAtIndexPath:
collectionView:layout:insetForSectionAtIndex:
collectionView:layout:minimumLineSpacingForSectionAtIndex:
collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
Important: Don't forget to prefix every mention of UICollectionView classes with PS. For example PSUICollectionView or PSUICollectionViewLayout.
EDIT 2: For a general understanding of UICollectionView refer to this excellent tutorial.
You can use you custom cells to achieve this functionality. One Cell with 10 subViews and 2nd with 6 subView. It will work for you.
you can get the view in the cell by tags i.e 1-6 and 1-10
Try this

How to create Multiple Themes/Skins for iphone apps? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I have an iphone app ready and approved by the app store. Now I want to create different themes for my app. Can someone please help me out, with info/links/steps on how to create themes for my app?
I want to create a Metal theme for the Boys and a Pink theme for the Girls. Again by theme I mean, the whole app(features and functionality) is gonna stay the same, but depending on who the user is(boy or girl), he/she can choose the theme they wish to see. And when the theme changes, only the images/Background/music will change according to the applied theme.
Thanks a lot!
This is quite difficult as apps don't have the equivalent of a css stylesheet.
First you need to work out what parts of the app you want to skin, and when you want to allow the user to swap skins.
I'm going to assume that you want to change images and font colours, and that it's okay if the user has to relaunch the app to change the skin (that will make things simpler for now).
Create a plist containing all your skinnable images and colours. The plist will be a dictionary with sensible, theme neutral key names for the images and colours (e.g. don't have a colour called "red", call it "primaryHeadingColor"). Images will be file names, and colours can be hex strings, e.g. FF0000 for red.
You'll have one plist for each theme.
Create a new class called ThemeManager and make it a singleton by adding the following method:
+ (ThemeManager *)sharedManager
{
static ThemeManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[ThemeManager alloc] init];
}
return sharedManager;
}
The ThemeManager class will have an NSDictionary property called "styles", and in the init method you will load the theme into your styles dictionary like this:
- (id)init
{
if ((self = [super init]))
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *themeName = [defaults objectForKey:#"theme"] ?: #"default";
NSString *path = [[NSBundle mainBundle] pathForResource:themeName ofType:#"plist"];
self.styles = [NSDictionary dictionaryWithContentsOfFile:path];
}
return self;
}
(Note: some people don't like doing a lot of work inside an init method. I've never found it to be an issue, but if you prefer, create a separate method to load the themes dictionary and call it from your app's setup code).
Notice how I'm getting the name for the theme plist from user defaults. That means the user can select a theme in your preferences and save it and the app will load that theme next time it is launched. I've put in a default theme name of "default" if no theme is selected, so make sure you have a default.plist theme file (or change the #"default" in the code to whatever your default theme plist is actually called).
Now that you've loaded your theme you need to use it; I'm assuming your app has various images and text labels. If you're loading and laying those out in code then this part is easy. If you are doing it in nibs then it's a bit trickier but I'll explain how to handle that later.
Now normally you would load an image by saying:
UIImage *image = [UIImage imageNamed:#"myImage.png"];
But if you want that image to be themable, you'll now need to load it by saying
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *imageName = [styles objectForKey:#"myImageKey"];
UIImage *image = [UIImage imageNamed:imageName];
That will look in your theme file for the themed image that matches the key "myImageKey" and will load it. Depending on which theme file you've loaded you'll get a different style.
You'll be using those three lines a lot so you may want to wrap them up in a function. A great idea would be to create a category on UIImage that declares a method called something like:
+ (UIImage *)themeImageNamed:(NSString *)key;
Then to use it you can just replace any calls to [UIImage imageNamed:#"foo.png"]; with [UIImage themeImageNamed:#"foo"]; where foo is now the theme key instead of the actual image name.
Okay, so that's it for theming your images. To theme your label colours, suppose you're currently setting your label colours by saying:
someLabel.color = [UIColor redColor];
You would now replace that with:
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *labelColor = [styles objectForKey:#"myLabelColor"];
someLabel.color = [UIColor colorWithHexString:labelColor];
Now you may have noticed that UIColor doesn't have a method "colorWithHexString:" - you'll have to add that using a category. You can Google for "UIColor with hex string" solutions to find code to do that, or I've written a handy category that does that and a bit more here: https://github.com/nicklockwood/ColorUtils
If you've been paying attention you'll also be thinking that instead of writing those three lines over and over, why not add a method to UIColor called:
+ (UIColor *)themeColorNamed:(NSString *)key;
Just like we did with UIImage? Great idea!
So that's it. Now you can theme any image or label in your app. You could use the same trick to set the font name, or any number of other potentially themable visual properties.
There's just one tiny thing we've forgotten...
If you've built most of your views as nibs (and I see no reason why you wouldn't) then these techniques aren't going to work because your image names and font colours are buried inside impenetrable nib data and aren't being set in your source code.
There are a few approaches to solve this:
1) You could make duplicate themed copies of your nibs and then put the nib names in your theme plist and load them from your theme manager. That's not too bad, just implement the nibName method of your view controllers like this:
- (NSString *)nibName
{
NSDictionary *styles = [ThemeManager sharedManager].styles;
return [styles objectForKey:NSStringFromClass([self class])];
}
Notice my neat trick of using the class name of the view controller as the key - that will save you some typing because you can just make a base ThemeViewController with that method and have all your themable view controllers inherit from it.
This approach does mean maintaining multiple copies of each nib though, which is a maintenance nightmare if you need to change any screens later.
2) You could make IBOutlets for all of the imageViews and labels in your nibs, then set their images and colors in code in your viewDidLoad method. That's probably the most cumbersome approach, but at least you don't have duplicate nibs to maintain (this is essentially the same problem as localising nibs btw, and pretty much the same solution options).
3) You could create a custom subclass of UILabel called ThemeLabel that automatically sets the font color using the code above when the label is instantiated, then use those ThemeLabels in your nib files instead of regular UILabels by setting the class of the label to ThemeLabel in Interface Builder. Unfortunately if you have more than one font or font colour, you'll need to create a different UILabel subclass for each different style.
Or you could be devious and use something like the view tag or accessibilityLabel property as the style dictionary key so that you can have a single ThemeLabel class and set the accessibility label in Interface Builder to select the style.
The same trick could work for ImageViews - create a UIImageView subclass called ThemeImageView that, in the awakeFromNib method replaces the image with a theme image based on the tag or accessibilityLabel property.
Personally I like option 3 best because it saves on coding. Another advantage of option 3 is that if you wanted to be able to swap themes at runtime, you could implement a mechanism where your theme manager reloads the theme dictionary, then broadcasts an NSNotification to all the ThemeLabels and ThemeImageViews telling them to redraw themselves. That would probably only take about an extra 15 lines of code.
Anyway, there you have a complete iOS app theming solution. You're welcome!
UPDATE:
As of iOS 5, it's now possible to set custom attributes by keyPath in Interface Builder, meaning that it's no longer necessary to create a view subclass for each themable property, or abuse the tag or accessibilityLabel for selecting styles. Just give your UILabel or UIImageView subclass a string property to indicate which theme key it should use from the plist, and then set that value in IB.
UPDATE 2:
As of iOS 6, there is now a limited skinning system built into iOS that allows you to use a property called the UIAppearance proxy to skin all instances of a given control class at once (there's a good tutorial about the UIAppearance APIs here). It's worth checking if this is sufficient for your skinning needs, but if not, the solution I outlined above still works well, and can be used instead, or in combination with UIAppearance.

xib file vs code

If you add something to the xib file in an xcode project, you can in fact make them generate little snippets in your code for things like event handlers.
what isn't generate however is some code that actually creates the object, assigns it to a viewport, initialises it etc. My question is, where is this code? is it hidden in the xib file?
and also, if I wanted to do all this myself programatically, what would it look like and where would i put it, for example in the open gl template xcode comes with?
I don't know where the code is but it's abstracted so you don't have to deal with it. You would put it in a ViewController file if you were to do it in code.
You would need something like;
CGRect viewRect = CGRectMake(0, 0, 100, 100);
UIView* myView = [[UIView alloc] initWithFrame:viewRect];
The following link explains how to deal with views in code.
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html
Interface Builder/Xcode doesn't generate code for the interface. Each xib contains the interface in serialized form (currently this is an xml like file, but note that this can change at any time! Also its converted to byte data file during compilation). At runtime a parser will read the file and create all the elements, hook everything up using KVC and then you have your interface.

Is it possible to design NSCell subclasses in Interface Builder?

I'm trying to subclass NSCell for use in a NSTableView. The cell I want to create is fairly complicated so it would be very useful if I could design it in Interface Builder and then load the NSCell from a nib.
Is this possible? How do I do it?
The question was about a subclass of NSCell; the other answers seem to be doing something else, likely taking advantage of UITableViewCell being a view.
NSCell is not a view. While laying a custom cell out in IB would be a useful thing to be able to do, I think the answer is basically "no, this is not possible". When you subclass NSCell, you're pretty much just doing your own drawing. There isn't support subcells, or parameterized auto layout (ala NSView's springs and struts), which is I suspect what you're looking for.
The only caveat is that you could design an NSCell subclass that did do layout of sub-elements and provided parameters for setting those subelements and all tweakable parameters. Then, you would need to write an IB plugin to make that cell and accompanying inspector available at design time in IB.
This, however, is probably harder than writing a little custom app that does more or less the same thing. Put an NSCell in a control in the middle of a window, and make yourself UI for tweaking the parameters you're interested in. Bindings can make this pretty straightforward for positioning stuff (i.e. bind an x value to a slider), though you will not get direct manipulation of the elements of course. When you're done, you could archive your cell and load the archive at runtime in your real app, or you could just log out the properties and set them in code in your app.
Some answers in this thread have gone off topic because they're talking about Cocoa Touch, when the original question was about Cocoa - the 2 APIs are quite different in this regard and Cocoa Touch makes it easy because UITableViewCell is a view subclass. NSCell isn't, and that's the problem
For information, I had to do something very similar in NSOutlineView recently - which is basically the same, but a little harder if anything because you have to deal with disclosure / collapse of levels. If you're interested in the code, I posted about it here: http://www.stevestreeting.com/2010/08/08/cocoa-tip-using-custom-table-outline-cells-designed-in-ib/
HTH
As Ken says, NSCells and NSViews are different, and you can only lay out NSView hierarchies in NIB, not NSCells (which don't have any explicit hierarchy).
On the other hand, there's nothing preventing you from having a hierarchy of NSViews and using that to draw your NSCell -- you could add them as a subview of your cell's parent view, tell them to display, and remove them from the window and nobody would be the wiser.
In this case, using a NIB would work, although it seems like a ton of hassle. Typically I've just replaced the object that takes NSCells with a custom one that takes my NSViews, but that means writing your own mouse-handling code, which is very touchy.
On the other hand, my approach lets you bind the views' values in NIB, so you don't have to do any extra work, which is cool.
In IB, start an empty XIB. Now go to the pallete and drag in a UITableViewCell, double click to bring up and edit.
include only the custom UITableViewCell (no other UIViews or other top level controls) - make sure it's a real UITableViewCell in IB, or you cannot set a reuse identifier (as opposed to casting a UIView in IB as your custom UITableViewCell class).
Then you can add lables or whatever you like within the cell, as well as setting the reuse identifier or set whatever disclosure indicator you might like.
To use, you provide code like this in the tableView:cellForRow:atIndexPath: method:
YourCustomCellClass *cell = (YourCustomCellClass *)[tableView dequeueReusableCellWithIdentifier:<IDYouSetInXIBFile>];
if ( cell == nil )
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:<YourXIBName> owner:self options:nil];
id firstObject = [topLevelObjects objectAtIndex:0];
if ( [ firstObject isKindOfClass:[UITableViewCell class]] )
cell = firstObject;
else cell = [topLevelObjects objectAtIndex:1];
}
If you have any labels or other controls you want to reference in your code, wire them in IB to your custom cell class - NOT the file's owner, which you do not ever need to set using the above code (you can leave it as NSObject).
Edit: I note you are really looking for an NSCell answer, but the code approach to using IB should be identical in Cocoa with the Cocoa Touch code I used above as loadNibNamed is a standard Cocoa call.
Joar Wingfors wrote an article for Stepwise a few years back on a related topic, Subviews in TableView Rows.
The principal technique is to create an NSCell that can host an NSView. If you were to do this, you could then design an NSView subclass in Interface Builder that you could embed anywhere you need that specific cell.
Another possibility, if you can target Leopard, is to see whether you need to use an NSTableView or whether you can use an NSCollectionView. Collection views deal directly in terms of "item views" rather than in cells, so they're much more straightforward to design in Interface Builder.
I found some interesting examples which I do not totally understand, though.
GitX extends the NSTextFieldCell in their PBIconAndTextCell, referencing this post.
WWDC 2009 - Session 110 "Presenting User Data with Table Views and Browsers" talks "Adding subviews" and "Custom cell editors". (I do not have the source code, though.)
Display an NSTextfieldCell containing text and an image within a NSTableView, #70
Display an NSTextfieldCell containing text and an image within a NSTableView, #71
The last 2 examples work with NSTableViewDataSource and NSTableViewDelegate. I would like to use Bindings an ArrayController in the InterfaceBuilder to connect other UI elements like text fields.
I stumbled into another discussion where Abizern points out PXListView by Alex Rozanski which looks very promising!
I am trying to implement a solution for the problem myself. Please find my project on github and the latest rendering problems over here.
I do it like this:
/* example of a silly way to load a UITableViewCell from a standalone nib */
+ (CEntryTableViewCell *)cell
{
// TODO -- this is really silly.
NSArray *theObjects = [[NSBundle mainBundle] loadNibNamed:#"EntryTableViewCell" owner:self options:NULL];
for (id theObject in theObjects)
if ([theObject isKindOfClass:self])
return(theObject);
NSAssert(NO, #"Could not find object of class CEntryTableViewCell in nib");
return(NULL);
}
However it isn't very efficient and if you're loading lot of data it might hurt you. Of course you should be using a reuseIdentifier which should force this code to only run a handful of times per table.
1) Create NSViewController TableViewCell.h
2) Create in TableViewCell.h some procedures like
-(void)setText:(NSString *)text image:(NSImage *)image
3) In Main class #import "TableViewCell.h"
4) In Main class in -(NSView *)tableView:viewForTableColumn:row: write:
NSImage *img = //some image
TableViewCell *cell = [[TableViewCell alloc] initWithWindowNibName:#"TableViewCell"];
cell.view.init;
[cell setText:#"some text" image:img];
return cell;
Hope this will help =)
I want to provide a more modern approach here.
Starting with iOS 5, UITableView has a method
(void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
Once you registered your NIB containing your cell, just use
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
to get a new cell. If a cell is available for reuse, it will be returned, otherwise a new cell is automatically created, and in that case this means loaded from the NIB file.
Add your UITableViewCell to your tableviewcontroller and declare an IBOutlet property:
#interface KuguTableViewController : UITableViewController {
IBOutlet UITableViewCell *customTypeCell;
}
#property (readonly) UITableViewCell *customTypeCell;
... then in cellForRowAtIndexPath you can just use your cell and set it to be reused:
static NSString *CellIdentifier = #"CustomCell"
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = customTypeCell;
cell.reuseIdentifier = CellIdentifier;