I'm currently initialising a view controller with a nib using initWithNibName however this method requires the full name of the nib, is it possible to use the first 4 characters rather than the full name.
The reason i would like to do this is because my nibs are named like this nib1_blahblahblah, nib2_blahblahblah, nib3_blahblahblah etc. Where blahblahblah is some text.
Thanks in advance.
If you really want to do that you can define some string constants:
You can then use it like a variable. Either give it a shorter name. Or give it the same name and code completion will take cars of the rest.
For example - in the implementation file:
NSString * const nib1_blahblahblah = #"nib1_blahblahblah";
NSString * const nib2_blahblahblah = #"nib2_blahblahblah";
Is it worth me telling you that better nib names would be a better idea?
Related
I'm currently helping a client that needs to change the language in their app due to certain governmental guidelines (medical vs wellness wording). Their app is huge and all the strings are contained in the code i.e. (stringWithFormat/hardcoded), none of it is in an external table. Meaning this would be a huge manual task.
At a undetermined point in the future the client believes they will receive approval to return to their current wording and will want to switch the strings back. Most of the changes will literally be switching a single problematic word to a less problematic word.
I thought that maybe if I could change the strings at run time based on a bool switch that it might eliminate the manual work involved and it would let me switch the language back when needed.
First attempt:
+ (instancetype)stringWithFormat:(NSString *)format, ...
{
va_list args;
va_start(args,format);
//todo check flag if we're changing the language
//todo replace problematic word from 'format'
NSString *result = [NSString stringWithFormat:format,args];
return result;
}
I first quickly coded up a category to override stringWithFormat to replace problematic words. I forgot that I would lose the original implementation of stringWithFormat. This resulted in endless recursion.
Next Attempt (subclass):
I started an attempt to subclass NSString but hit a stackoverflow post saying that if my solution was to subclass a class cluster then I didn't understand my problem because subclassing a class cluster is almost never done.
Final Option (wrapper):
My final attempt would be to write a wrapper class but that kind of defeats the original purpose which was to avoid having to manually seek out each string in the app.
I'm not really sure how to approach this problem anymore. What do I do if I need to add/override functionality to one of the core classes.
There is nothing wrong with the idea of your first attempt, just a little mistake in its implementation. Change:
NSString *result = [NSString stringWithFormat:format,args];
to:
NSString *result = [NSString alloc] initWithFormat:format arguments:args];
which is the expansion of stringWithFormat: and the interception will work.
Thoughts about class clusters are a red herring in this particular situation, the front class for a cluster (NSString) must provide implementations for class methods (+stringWithFormat:), so you can use a simple category to intercept them.
However, having intercepted +stringWithFormat: be careful. A simple test will show you it is used a lot by the frameworks and you do not wish to break them - as my first simple test did by simply changing "d" to "c", which changes "window" to "wincow", which in turn broke the binding setup of Xcode's default app which binds the property "window"...
If you are changing health-related words you might be OK, whole strings would be better.
A better approach might be to simply write a RE to match all the literal strings in the code and replace them by function(string) for some function (not method) you write to do the conversion.
HTH
There is a much simpler solution that seems like a better fit. Use NSLocalizedString, with keys instead of actual strings:
displayString *NSString = NSLocalizedString(#"displayString", nil);
cancelButtonTitle *NSString = NSLocalizedString(#"cancelButtonTitle", nil);
Then create a Localizable.strings file in your app, and define the actual values that should be displayed:
"displayString" = "The string to display in English"
"cancelButtonTitle" = "Cancel"
You put the Localizable.strings file in your app bundle and the app uses it to do string replacements at runtime.
You can also define different versions of Localizable.strings for different languages, so, for example, if the user has set their language to Spanish, the call to NSLocalizedString() gives you the Spanish-language version.
(As you've mentioned, NSString is a class cluster. That means that it is a public interface to a variety of different private subclasses. You can't be sure what private subclass you get when you create an NSString, and that makes it a bad idea to try to subclass it.)
For hardcoded strings you have no other way but to modify those manually by assigning it to a string converter class of some sort. So those for:
yourmom.text = #"Hi Mom";
yourdad.text = [NSString stringWithFormat:#"%# and Dad!",yourmom.text];
You need to change these kind of assignments to something like
yourmom.text = [StringConverter string:#"Hi Mom"];
yourdad.text = [StringConverter string:#"%# and Dad!" placeHolder:yourmom.text];
As for strings in storyboards or xibs, you can change them by iterations loop in viewdidload. Good luck.
I am new to Objective-C and this Smalltalk syntax is quite frustrating for a new comer. I am trying to call the following method declaration in an IF statement:
-(BOOL) string:(NSString *)string1 containsCharInString:(NSString *)string2
When call it I do it like this, which I believe should work:
if([string1 containsCharInString: word2]) {...}
1. The autocomplete does not even recognise this as a method of my class
2. I get the following error when attempting to call it:
No visible #interface for NSString declares the selector 'containsCharInString'.
To call this method
-(BOOL) string:(NSString *)string1 containsCharInString:(NSString *)string2
you would say
[someObject string:someString containsCharInString:someOtherString]
Do you see?
I don't know the class of someObject because I don't know the class on which you have defined that method. But what I'm trying to show you is that every parameter counts. You can't just throw one away.
To put it another way: there is no selector containsCharInString: because that's not the name of the method you created. The method you created is called string:containsCharInString:. That may not be the method you wanted to create, but that is the method you did create.
You could also do this rather than writing your own method.
NSString* aString = #"some string";
if ( [aString rangeOfString:#"different string"].location == NSNotFound )
It sounds to me like you're trying to extend the behavior of the NSString class.
As others have pointed out, NSString already has very efficient methods like rangeOfCharacterFromSet to do this. Explore using one of those methods instead of writing custom code. If you're going to be doing it over and over, create an NSCharacterSet object and save it for reuse.
To your question, though, about making a call like this work:
if([string1 containsCharInString: word2]) {...}
One way to do this is to make string1 a custom object, possibly a subclass of NSString (not a good idea for beginners - NSString is what's called a "Class cluster", and those are tricky to subclass).
Probably the easiest way to do it would be to create a category of NSString. A category is a quirk of Objective-C that lets you add methods to existing classes without subclassing them. Categories can't add instance variables, just methods.
The interface for your category might look like this:
#interface NSString (containsChar)
(BOOL) containsCharInString: (NSString *) string;
#end
And then provide an implementation for the category method(s).
I am mostly writing this for other newbie folks and to remind myself in a few years when I forget what I did
I had a long list of localized strings in the application I was building. I really hated the lack of readability as I plodded my way through a whole bunch of statements like
[_myUILabelObject setText:NSLocalizedStringFromTable(_myString, applicationLanguage,nil)]
Then I was going to need another one that was different for UIButtons and UITextView and so on. There had to be a better way. What I wanted was something like
[_myUILabelObject translateMe:#"This text");
So here's what I did:
I created a bunch of class extensions (technically categories) for each object: UIButton, UILabel, UITextView... whatever I needed.
Each one has a simple .h file that simply declares that the extension is related to the main class. Here's an example for my translateMe for UITextField:
#interface UITextField (translateMe)
- (void) translateMe: (NSString *) usingString;
#end
The first line tells the compiler that translateMe is going to be an extension of UITextField.
The second line tells the compiler that translateMe is expecting to be a method of UITextField that receives an NSString that I've called usingString.
Then in the .m implementation file, I've done this:
extern NSString *applicationLanguage;
- (void) translateMe: (NSString *) string {
[self setText:NSLocalizedStringFromTable(string, applicationLanguage,nil)];
[self sizeToFit];
}
The applicationLanguage string is a string that I set elsewhere based on the user's selection of language and it essentially is the name of the .strings file.
The rest of the code here simply includes all the laborious coding that I wanted to avoid in my ViewController's code.
Now the magic here is that one can create similar combinations of the .h and .m files for every object you have in your ViewControllers. If you call all of the methods translateMe, then you don't have to worry at coding time which of the objects you're actually calling. The result is you get something that looks simple like this:
[_standardDemoPlanTitle translateMe: #"Standard Demo Plan"];
[_linkSampleDemoVideoButton translateMe: #"Link to sample demo"];
[_linkCustomerPresentationButton translateMe: #"Link to customer presentation"];
[_personalNotesTitle translateMe: #"Your Personal Notes"];
[_theSaveButton translateMe: #"Save"];
[_emailPlanButton translateMe: #"Email this plan to a colleague"];
Note that the fact that some are buttons and some are labels (Titles) doesn't matter at all.
As an additional short cut, I learned from another post that it is possible to deal with long sections of text in a short way too.
Instead of:
[_longTextRequiredByLawyers translateMe: #"blah blah blah blah blah forever..."];
What I can do is to put in a short handle for the text in the implementation file and put the longer text in the .strings file. Here's an example:
[longTextRequiredByLawyers translateMe: #"#lawyerText"];
by the way, the #-sign is not required, but I used it throughout my .m files as a personal reminder so that I knew that what was going to inserted did not necessarily match with what was in the prototype in the interface builder.
Then my english.strings file included something like this:
"#lawyerText" = "When in the course of human events it becomes necessary for one people to dissolve the bonds which have connected them with another and to assume among the powers of the earth the separate and equal station to which the laws of God...";
and my spanish.strings file had the translation:
"#lawyerText" = "En México no hay abogados, entonces el texto es mas corto.";
You could do the same thing for Albanian and Klingon.
So, key tricks (or good programming ideas here):
Use Cateogries to extend the class definitions for the UI objects you have on your pages. Those categories can include everything you need to make your main code more readable.
Name the method the same thing in every extension so that you don't have to think about it when you do the magic in your .m files.
Use short handles for long strings of text and jam the longer stuff into the .strings files.
One thing I wish I had figured out:
I now have a long list of .h files that I now have to #include at the top of all of my ViewController .m files. I wish that I could have a .h file that #includes all of the translateMe .h files... kind of a nested .h.
UIButton+translateMe.h
UITextView+translateMe.h
UILabel+translateMe.h
UIButton+translateMe.h
and so on
Wouldn't it be nice to have a single .h file, perhaps allMyTranslates.h that has the list above inside of it, and then I just include allMyTranslates.h in the code I am writing.
I would end up replacing:
#include "UIButton+translateMe.h"
#include "UITextView+translateMe.h"
#include "UILabel+translateMe.h"
#include "UIButton+translateMe.h"
#include and so on
with this
#include "allMyTranslateHeaders.h"
Well, there you go... a few notes from a newbie... All the experts have been helpful of course, but every once in a while we need a note like this one to get us over the hump!
I am facing issue for a method, which I have using for other NSString and working fine.
See this image.
Here html is NSString, I have used it in other project, like a clone of this, its working fine there, even I wrote like this, working fine in that project,
html = [html stringByConvertingHTMLToPlainText];
but here in this method , both the ways giving this warning.
What that mean and how can I solve it
?
If htmlis typed as an id, but you know that it's in fact an NSString, try casting it to NSStringand call your selector from there (id type to NSString).
That said, the reported error often occurs when your method is not declared in your header file (https://stackoverflow.com/a/13154244/865688). Did you forget to #import?
"stringByConvertingHTMLToPlainText" is not a standard method defined in the NSString class. It is s commonly used category defined in the MWFeedParser project.
Make sure you have imported the category using the right syntax. Refer: https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html Search for text "import the category header file" read around that area if not the whole chapter.
This question already has answers here:
What is self in ObjC? When should i use it?
(6 answers)
Closed 9 years ago.
So, I just started learning Objective-C and I've come across this "self" thing. I've only ever used C, but I think it's used in java too maybe? Can someone explain? Here's an example:
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [sender currentTitle];
UILabel *myDisplay = [self display]; //why this?
}
Why isn't it just this?
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [sender currentTitle];
UILabel *myDisplay = display; //why not like this?
}
display is a UILabel *
[self display], or self.display, refers to a property / method (property is just a shortcut for get/set method anyway) if you have something like this in the .h file
#property (weak, nonatomic) UILabel* display;
or
-(UILabel*)display;
Just display, or self->display refers to an instance variable. This is valid when you have declared an instance var like this:
#implementation MyClass {
UILabel* display;
}
If you have declared the property display in the .h file and haven't changed its default, the corresponding instance var will be _display (note the underscore), in which case the following will be the same:
self.display and self->_display
In this case it's an objective C messaging thing. When you see the brackets it's doing this:
[Object Message]
Basically self is the object and display is the message your sending it. Sending it a message is like a method call in another language, but a little different under the hood. So something like this:
[self doSomethingCool];
in objective C would translate to something like this in another language:
this.doSomethingCool();
of course if running a method on another object you'll replace self with that object like:
[myObject doSomethingCool];
in a lot of languages you don't really need to have the "this" in front of your method call, it's implied that if you don't include it you're running the method in the object you're working with. I got burned pretty early on when I started with something similar. I had a call to a datalayer method where you could save an object and it would give you an integer back. When I was saving the object I didn't put the self in front of the method call and it was essentially generating a new object and saving it and I wasn't getting the right integer back.
Using "self" just explicitly tells it "I'm using THIS object". Same thing with properties, I always use "self.MyProperty" instead of "MyProperty" because I want to be explicit and make sure I'm using the MyProperty of the object I'm working in. It's semi rare for a defect like that to hit you, where you expect to be using a certain object and the environment thinks you're using another, but man when you run into one it's a head scratcher because everything looks right.
The word self refers to the current object, which is your view controller instance in this case, and combining it with a method name, which is display, means you are sending the message display to self which is the view controller. This will invoke the method display declared in your view controller instance.
You might declare the display method in your view controller, for example:
- (UILabel)display
{
//your display method implementation returning UILabel instance
}
For the second one, it means you are referring to display variable. For example:
UILabel *display = [[UILabel alloc] init];
display is not a UILabel * - it might be a property with that type, or a method which returns a value of that type, but these a rather different things.
You need to go an read something about object oriented programming. The self in Objective-C is the current object reference, other OO languages call it this - both Java and C++ use that name. Understanding objects and methods is fundamental to using any of these languages.
There's a very good explanation of this here:
http://useyourloaf.com/blog/2011/02/08/understanding-your-objective-c-self.html
The key section for your question is the section on Objective-C 2.0 dot syntax:
Objective-C Dot Syntax
The dot syntax was introduced with Objective-C 2.0 and generates a lot
of debate. A number of experienced and long time Cocoa programmers
recommend avoiding it completely. Others such as Chris Hanson have a
different view about when to use properties and dot notation.
Whichever side of the argument you fall I guess the main thing is to
be consistent.
Anyway the main thing to understand about the dot syntax is that the
following two statements are doing the same thing:
self.timestamp = [NSDate date];
[self setTimestamp:[NSDate date]];
The dot is just a shortcut for the more traditional Objective-C method
call. Any time you see a dot you can replace it with the equivalent
square bracket method call syntax. It is important to understand
however that this is not the same as writing the following:
timestamp = [NSDate date]; Without the self object and the dot we are
no longer sending an object a message but directly accessing the ivar
named timestamp. Since this bypasses the setter method we will
overwrite the timestamp ivar without first releasing the old NSDate
object. We will also not retain the new object that we are assigning.
Both of these situations are bad!
Keep in mind that the examples were written without using ARC, so there's a lot of references to memory management, retain, release etc. It is however useful to see these examples so that you have some idea of what ARC is doing in the background.
In your example, you are not referring to the actual display property with [self display] you are in fact referring to an instance method of the "self" object which in this case is your UIViewController.