What I want to have happen:
Items in array show up in a label. Each item in the array will have it's own label.
I eventually want to add more items.
And make it so that index 0 of thisArray can be added to index 0 of that array; display in a label;
object at index 1 in thisArray can be added to object at index 1 in thatArray and then the result can be displayed in a separate label,
and so on
To appear on a button click.
I'm having a lot of trouble implementing this and I feel like it is a lot simpler than I am making it. Any insight would be appreicated!!
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.steakArray = [[NSArray alloc] initWithObjects: #"800", #"50", nil];
//NSString *name;
NSString *calories = [_steakArray objectAtIndex:0];
NSString *fat = [_steakArray objectAtIndex:1];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//this doesn't work, this is what I want to have happen though.
//click button, show this in label.
- (IBAction)steak:(id)sender {
self.infoLabel.text = calories, fat;
}
#end
You need to look at this documentation https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/FormatStrings.html
You can use String with Format:
NSString *string1 = [NSString stringWithFormat:#"A string: %#, a float: %1.2f",
#"string", 31415.9265];
// string1 is "A string: string, a float: 31415.93"
NSNumber *number = #1234;
NSDictionary *dictionary = #{#"date": [NSDate date]};
NSString *baseString = #"Base string.";
NSString *string2 = [baseString stringByAppendingFormat:
#" A number: %#, a dictionary: %#", number, dictionary];
// string2 is "Base string. A number: 1234, a dictionary: {date = 2005-10-17 09:02:01 -0700; }"
Even if you are getting calories in string, you can use the following code to add and display in the label
NSString *calories1 = [_steakArray objectAtIndex:0];
NSString *calories2 = [_steakArray2 objectAtIndex:0];
int sum = [calories1 intValue] + [calories2 intValue];
self.infoLabel.text = [NSString stringWithFormat:#"%d",sum];
Related
absolute beginner here. I'm trying to teach myself Xcode using several different sources. My current lesson, I'm just trying to capitalize each word in a string. For some reason, I'm not given the option of using addObject, even though I've resorted to copying line for line what's in the book! Here's the code I'm using, I'm just typing it into the ViewController.m. I haven't touched the header file.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *myString = #"How much wood could a woodchuck chuck";
NSArray *wordsInSentence = [myString componentsSeparatedByString:#" "];
NSLog(#"%#", wordsInSentence);
NSMutableArray *capitalizedWords = [[NSMutableArray alloc] init];
for (int word =0; word < [wordsInSentence count]; word++)
{
NSString *uncapitalizedWords = [wordsInSentence objectAtIndex:word];
NSString *capitalizedWords = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWords];
}
NSLog(#"%#", capitalizedWords);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
My problem is with
[capitalizedWords addObject:capitalizedWords];
When I begin typing, it doesn't even show addObject in the drop down box as an option, the only option I have is addObserver.
Any and all help would be much appreciated, Thanks
The problem is that you have two variables with the same name, capitalizedWords. One is the mutable array, the other is the string. And, thus, when you use capitalizedWords inside that for loop, it's using the string rendition. I would suggest renaming the string variable, e.g., replace:
NSString *uncapitalizedWords = [wordsInSentence objectAtIndex:word];
NSString *capitalizedWords = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWords];
with
NSString *uncapitalizedWord = [wordsInSentence objectAtIndex:word]; // renaming this isn't critical, but using singular case makes it more clear
NSString *capitalizedWord = [uncapitalizedWord capitalizedString]; // renaming this fixes the problem
[capitalizedWords addObject:capitalizedWord];
The problem is that you are naming the string the same as the array.
Try changing
NSString *capitalizedWords = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWords];
to:
NSString *capitalizedWordsString = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWordsString];
When you're declaring the string with the same name as the array you're shadowing the array and Xcode will see capitalizedWords as a string, not an array. That's why it doesn't present you with the addObject method.
I created a method which gets data from a server, everything works fine except when I try to set string to for example UILabel or UITextView, nothing shows and changed ! here is my code :
- (void)viewDidLoad
{
[super viewDidLoad];
[self getDataFromURL:#"http://somesites.net/panel/services?action=events&num=1"
setTitle:_eTitle1.text image:_eImage1 description:_eNews1.text];
}
Getting Data :
-(void)getDataFromURL:(NSString*)url setTitle:(NSString*)eTitle
image:(UIImageView*)eImages description:(NSString*)eDescriptions {
NSURL *URL = [NSURL URLWithString:url];
NSError *error1;
NSString *strPageContent = [NSString stringWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error1];
strPageContent = [strPageContent gtm_stringByUnescapingFromHTML];
if ([strPageContent rangeOfString:#"<plist version=\"1.0\">"].location != NSNotFound) {
NSRange range = [strPageContent rangeOfString:#"<plist version=\"1.0\">"];
strPageContent = [strPageContent substringWithRange:NSMakeRange(range.location+range.length, strPageContent.length-(range.location+range.length))];
strPageContent = [strPageContent stringByReplacingOccurrencesOfString:#"</plist>" withString:#""];
}
NSError *error = nil;
NSDictionary *dict = [XMLReader dictionaryForXMLString:strPageContent options:XMLReaderOptionsProcessNamespaces
error:&error];
if ([dict count]>0) {
NSDictionary *dictInner = [dict objectForKey:#"dict"];
NSArray *arrValues = [dictInner objectForKey:#"string"];
NSString * strTitle = [[arrValues objectAtIndex:0] objectForKey:#"text"];
NSString *strImage = [[arrValues objectAtIndex:1] objectForKey:#"text"];
NSString * strDescription = [[arrValues objectAtIndex:2] objectForKey:#"text"];
eTitle = strTitle;
eDescriptions = strDescription;
// [eImages setImageWithURL:[NSURL URLWithString:strImage]
// placeholderImage:[UIImage imageNamed:#"loadingPad.jpg"]];
NSLog(#"Title: %# | Image: %# | Desc: %#",eTitle,strImage,eDescriptions);
}
}
compiler gives me the right information ! but these string could not set to my lable , IF I put my lable's string into the method it works !!! :
_eTitle1.text = strTitle ;
It's completely normal: when you pass the "text" object to the method, you are passing the pointer to it. Assigning to it directly another NSString object will just assign a new pointer. In order to have side effect on a string you gotta use NSMutableString, but the UILabel has just an immutable NSString for the text attribute. So the only solution is to pass the UILabel or pass an initialized empty mutable string inside the method, change the content via [eTitleText setString:strTitle] and then, outside the method, assign it to the UILabel text attribute.
So, either you change the method like this (as you already did):
-(void)getDataFromURL:(NSString*)url setTitle:(UILabel*)eTitle
image:(UIImageView*)eImages description:(NSString*)eDescriptions {
...
eTitle.text = strTitle;
...
and using it like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[self getDataFromURL:#"http://somesites.net/panel/services?action=events&num=1"
setTitle:_eTitle1 image:_eImage1 description:_eNews1.text];
}
Or you can go this other way:
-(void)getDataFromURL:(NSString*)url setTitle:(NSMutableString*)eTitle
image:(UIImageView*)eImages description:(NSString*)eDescriptions
...
[eTitle setString:strTitle];
...
and using it like this:
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableString *titleText = [NSMutableString new];
[self getDataFromURL:#"http://somesites.net/panel/services?action=events&num=1"
setTitle:titleText image:_eImage1 description:_eNews1.text];
eTitle1.text = titleText;
}
I have a NSString like this one:
NSString* allSeats = #"1_Male,2_Female,3_Female,4_Male";
I want to split the NSString based on the keywords _Male & _Female and then make two separate strings like these:
NSString* maleSeats = #"1,4";
NSString* femaleSeats = #"2,3";
based on the contents of allSeats variable declared above.
How it will be possible to split NSString and then make 2 seperate strings?
You have to do it yourself. There is no "all done" solution. There are a few ways to do it.
Note: I didn't try my code, I just wrote it, it may don't even compile. But the important thing is that you get the whole idea behind it.
One way could be this one:
NSString *maleSufffix = #"_Male";
NSString *femaleSufffix = #"_Female";
NSMutableArray *femaleSeatsArray = [[NSMutableArray alloc] init];
NSMutableArray *maleSeatsArray = [[NSMutableArray alloc] init];
NSArray *array = [allSeats componentsSeparatedByString:#","];
for (NSString *aSeat in array)
{
if ([aSeat hasSuffix:maleSuffix])
{
[maleSeatsArray addObject:[aSeat stringByReplacingOccurencesOfString:maleSuffix withString:#""]];
}
else if ([aSeat hasSuffix:femaleSuffix])
{
[femalSeatsArray addObject:[aSeat stringByReplacingOccurencesOfString:femaleSuffix withString:#""]];
}
else
{
NSLog(#"Unknown: %#", aSeat);
}
}
NSString *maleSeats = [maleSeatsArray componentsJoinedByString:#","];
NSString *femaleSeats = [femaleSeatsArray componentsJoinedByString:#","];
Of course, you could use different methods on array, enumerating it, use a NSMutableString instead of a NSMutableArray (for femaleSeatsArray or maleSeatsArray, and use adequate methods then in the for loop).
I derived an idea from Larme's Clue and it works as :
Make a method as and call it anywhere :
-(void)seperateSeat
{
maleSufffix = #"_Male";
femaleSufffix = #"_Female";
femaleSeatsArray = [[NSMutableArray alloc] init];
maleSeatsArray = [[NSMutableArray alloc] init];
array = [self.selectedPassengerSeat componentsSeparatedByString:#","];
for (aSeat in array)
{
if ([aSeat hasSuffix:maleSufffix])
{
aSeat = [aSeat substringToIndex:[aSeat length]-5];
NSLog(#"%# is value in final seats ::",aSeat );
[maleSeatsArray addObject:aSeat];
}
else if ([aSeat hasSuffix:femaleSufffix])
{
aSeat = [aSeat substringToIndex:[aSeat length]-7];
NSLog(#"%# is value in final seats ::",aSeat );
[femaleSeatsArray addObject:aSeat];
}
}
totalMales = [maleSeatsArray componentsJoinedByString:#","];
totalFemales = [femaleSeatsArray componentsJoinedByString:#","];
NSLog(#"maleSeatsAre::::%#",totalMales);
NSLog(#"maleSeatsAre::::%#",totalFemales);
}
I think that I'm missing some fundamental knowledge on Xcode Objective C programming standards. Unfortunately I couldn't find the appropriate solution to my problem.
The problem is that when I try to keep data in an array of objects it becomes impossible to keep them separately. Adding new objects overwrites the previous objects in array. Here is some code about that:
CustomObject.m file:
#import "CustomObject.h"
NSString * title;
NSString * detail;
#implementation CustomObject
- (void) initCustomObjectWithValues : (NSString *) iTitle : (NSString *) iDetail {
title = [NSString stringWithString:iTitle];
detail = [NSString stringWithString:iDetail];
}
- (NSString *) getTitle {
return title;
}
- (NSString *) getDetail {
return detail;
}
#end
viewDidLoad function in ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myMutableArray = [[NSMutableArray alloc] init];
for (int i=0; i<10; i++) {
NSString * tempTitle = [#"title " stringByAppendingString:[NSString stringWithFormat:#"%d",i]];
CustomObject * myCustomObject = [[CustomObject alloc] init];
[myCustomObject initCustomObjectWithValues :[NSString stringWithFormat:#"%#",tempTitle]
:[#"detail " stringByAppendingString:[NSString stringWithFormat:#"%d",i]]];
[myMutableArray addObject:myCustomObject];
}
for (int i=0; i<10; i++) {
NSLog(#"%#",[[myMutableArray objectAtIndex:i] getTitle]);
NSLog(#"%#",[[myMutableArray objectAtIndex:i] getDetail]);
NSLog(#"----------------------------");
}
}
Here, myMutableArray is defined at the top of the ViewController.m file. (To make it global and can be used in other functions in future)
Here what I've got in the logs:
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
As far as I understand each new added object overwrites the olds. First I thought that they are referring to same allocated memory but in debug tool myMutableArray seems like this:
Printing description of myMutableArray:
<__NSArrayM 0x8d8cb60>(
<CustomObject: 0x8d8e990>,
<CustomObject: 0x8d8dd40>,
<CustomObject: 0x8d8d2e0>,
<CustomObject: 0x8d8d470>,
<CustomObject: 0x8d8d350>,
<CustomObject: 0x8d8ddf0>,
<CustomObject: 0x8d8df00>,
<CustomObject: 0x8d8df40>,
<CustomObject: 0x8d8dff0>,
<CustomObject: 0x8d8e0c0>
)
Does anyone have an idea about the solution. It should be something very basic but I can't catch the problem.
Thank you all in advance
using
NSString * title;
NSString * detail;
outside of the #interface part creates global variables. When you assign a variable to title or detail you don't set an instance variable of your object, you change those global variables. And since they are global, they are the same for all objects that reference them.
Turn those global variables into instance variables, or even better use #property.
Your code is bad objective-c overall.
You should not use get in getters that return variables. You should not have methods that start with init and don't return self. You should only call init in [[Foo alloc] init...] situations. You should avoid unnamed parameters in your methods.
And there is no need to create strings from strings from strings.
Here is how I would write it:
// CustomObject.h
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString * title;
#property (copy, nonatomic) NSString * detail;
- (id)initWithTitle:(NSString *)title detail:(NSString *)detail
#end
// CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
- (id)initWithTitle:(NSString *)title detail:(NSString *)detail {
self = [super init];
if (self) {
// use stringWithString: to create #"" strings when title is nil
// if nil is a valid value for those variables you should use
// _title = [title copy];
_title = [NSString stringWithString:title];
_detail = [NSString stringWithString:detail];
}
return self;
}
#end
for (int i=0; i<10; i++) {
NSString *tempTitle = [NSString stringWithFormat:#"title %d",i];
NSString *tempDetail = [NSString stringWithFormat:#"detail %d",i];
CustomObject * myCustomObject = [[CustomObject alloc] initWithTitle:tempTitle detail:tempDetail];
[myMutableArray addObject:myCustomObject];
}
for (int i=0; i<10; i++) {
CustomObject *object = myMutableArray[i];
NSLog(#"%#", object.title);
// or NSLog(#"%#", [object title]); if you don't like dot-notation.
NSLog(#"%#", object.detail);
NSLog(#"----------------------------");
}
Note: As I've posted this question, it's night time here in my place. If I haven't responded to comments or answers, I'll do it in the morning. Thank you for your understanding :-)
The idea is to format UILabel as phone number (US Format) as the user touches each UIButton.
There is a keypad layout and as the user touches each numeric key, the label displays it and at formats the resulting string on the fly. I have written a piece of code which I am not very proud of. I hope someone could help me in doing this in much better way or different approach.
The image should give you a fair idea of how it looks like. Now, getting to the logic:
In the code below, keyOne is the IBAction to append the numbers to UILabel, keyBack is the one to delete it.
So this is how it works for entering a phone number:
If I enter 1234567890:
after 1,2,3 the label is modified as 123- and gets assigned to string
Now string will be 123- and I continue touching 4567890
after 123-4567, when I touch 8, label is modified as (123)456-78 and gets assigned to string
Now string will be (123)456-7890
If I wish, I could continue entering more numbers.
As per requirement, the number should lose its formatting.
i.e. After (123)456-7890 if I press 1, it should become 12345678901
Now, the trick is to undo the steps in exact same way. Here is the pickle.
If I entered only 1234567890, _phoneNumber.text would be (123)456-7890 and pressing back three times should result in 123-4567. Then pressing back four times should result in 123
However, if I entered 12345678901, _phoneNumber.text would go to (123)456-7890, then lose formatting to become 12345678901 and when I press back once, it should become (123)456-7890 and so on.
If you whip up a new project of type single view application and paste the stuff below, (of course after creating buttons and connecting 0-9 with keyOne, back with keyBack) you would see that it works fine. But as I mentioned above, I have written a piece of code which I am not very proud of. I feel there should be a simpler way to do this.
ViewController.h
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *lengthParam;
#property (weak, nonatomic) IBOutlet UILabel *phoneNumber;
- (IBAction)keyOne:(id)sender;
- (IBAction)keyBack:(id)sender;
- (IBAction)clearAll:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController () {
BOOL isReformatted;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (IBAction)keyOne:(id)sender {
UIButton *btn = (UIButton *)sender;
NSString *str = [NSString stringWithString:self.phoneNumber.text];
str = [str stringByAppendingString:btn.titleLabel.text];
self.phoneNumber.text = str;
if(str.length==4) self.phoneNumber.text = [self insertMinus:str]; //before the 4th number, insert '-' as a first step
if(str.length==9) self.phoneNumber.text = [self insertParenthesis:str]; //remove - before the 4th number, encapsulate first 3 numbers inside () and add - at end of current number
if(str.length>13) self.phoneNumber.text = [self plainFormat:str]; //if user enter more than 10 numbers, remove formatting
self.lengthParam.text = [NSString stringWithFormat:#"%d",self.phoneNumber.text.length];
}
- (IBAction)keyBack:(id)sender {
NSString *str = [NSString stringWithString:self.phoneNumber.text];
NSString *newStr = nil;
if(str.length>0) { //check for empty string
if(str.length>11 && isReformatted==NO) newStr = [str substringToIndex:str.length-1]; //string.length > 10 which means plainFormat was called earlier and reFormat isn't called yet
else if(str.length==11 && isReformatted == NO) { //string length is now 11
if([str characterAtIndex:0]!='(') newStr = [self reFormat:str]; //if entered string is 12345678901, it is not reFormatted. remove last 1 and reFormat it as (123)456-7890
else newStr = [self removeParenthesis:str]; //entered string itself is (123)456-78 so transform it as 123-4567
} else { //we are dealing with a reformatted string of length 11 now.
newStr = [str substringToIndex:str.length-1]; //String is (123)456-78, remove 8 and apply one of the below rules
if(newStr.length==10) { //transform (123)456-7 as 123-4567
newStr = [str substringToIndex:str.length-1];
newStr = [self removeParenthesis:str];
}
if(newStr.length==4&&[newStr characterAtIndex:3]=='-') newStr = [self removeMinus:str]; //transform 123-4 to 123
}
self.phoneNumber.text = newStr;
self.lengthParam.text = [NSString stringWithFormat:#"%d",self.phoneNumber.text.length];
}
}
- (IBAction)clearAll:(id)sender {
self.phoneNumber.text = #"";
self.lengthParam.text = [NSString stringWithFormat:#"%d",self.phoneNumber.text.length];
}
- (NSString *)insertMinus:(NSString *)str {
return [NSString stringWithFormat:#"%#-%#",[str substringToIndex:3],[str substringFromIndex:3]];
}
- (NSString *)insertParenthesis:(NSString *)str {
NSString *c1 = [NSString stringWithFormat:#"(%#)",[str substringToIndex:3]];
NSString *c2 = [NSString stringWithFormat:#"%#-%#",[str substringWithRange:NSMakeRange(4, 3)],[str substringFromIndex:7]];
return [NSString stringWithFormat:#"%#%#",c1,c2];
}
- (NSString *)plainFormat:(NSString *)str {
isReformatted = NO;
str = [str stringByReplacingOccurrencesOfString:#"(" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#")" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#"-" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#" " withString:#""];
return str;
}
- (NSString *)reFormat:(NSString *)str {
isReformatted = YES;
NSString *c1 = [NSString stringWithFormat:#"(%#)",[str substringToIndex:3]];
NSString *c2 = [NSString stringWithFormat:#"%#-%#",[str substringWithRange:NSMakeRange(3, 3)],[str substringWithRange:NSMakeRange(6, 4)]];
return [NSString stringWithFormat:#"%#%#",c1,c2];
}
- (NSString *)removeParenthesis:(NSString *)str {
str = [self plainFormat:str];
NSString *newStr = [NSString stringWithFormat:#"%#-%#",[str substringToIndex:3],[str substringWithRange:NSMakeRange(3, 4)]];
return newStr;
}
- (NSString *)removeMinus:(NSString *)str {
str = [self plainFormat:str];
NSString *newStr = [NSString stringWithFormat:#"%#",[str substringToIndex:3]];
return newStr;
}
#end
If you are eager to go down the road of rolling your own phone number formatter, I would consider doing the follow:
Store the user input without formatting in a string.
After each key press, reformat the display string.
Something like:
#property (nonatomic, copy) NSMutableString *backingString;
#property (weak, nonatomic) IBOutlet UILabel *phoneNumber;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
backingString = [NSMutableString string];
}
- (IBAction)keyOne:(id)sender
{
UIButton *btn = (UIButton *)sender;
[self.backingString appendString:btn.titleLabel.text];
[self formatBackingString];
}
- (IBAction)keyBack:(id)sender
{
[self.backingString deleteCharactersInRange:(NSRange){self.backingString.length - 1,1}];
[self formatBackingString];
}
- (void)formatBackingString
{
// Do your phone formatting here
NSMutableString *formattedString = [NSMutableString string];
NSInteger length = self.backingString.length;
if (length < 3)
{
[formattedString appendString:self.backingString];
}
else if (length == 3)
{
[formattedString appendFormat:#"%#-", self.backingString];
}
else if (length < 8)
{
// etc...
}
self.phoneNumber.text = formattedString;
}
You could also look at NSNumberFormatter.