I want to append a string only once when the view appears. Problem is, when I am moving back from previous view, text gets appended repeatedly. because I have kept the string appending code in viewWillAppear.
Here is the code,
if (!sharedController.perimeterFencesFreeOfHazard) {
NSString *origText = _messageLabel.text;
count++;
_messageLabel.text = [origText stringByAppendingString:#"\n • Perimeter fences & signs"];
//count++;
}
How to ensure string gets appended only once?
So you need a flag, which tells you if the text has been added for that view or not.
MyViewController.h:
#interface MyViewController : UIViewController {
BOOL _textAdded;
}
#end
MyViewController.m:
#implementation MyViewController
- (void)viewWillAppear {
if (!sharedController.perimeterFencesFreeOfHazard && !_textAdded) {
NSString *origText = _messageLabel.text;
count++;
_messageLabel.text = [origText stringByAppendingString:#"\n • Perimeter fences & signs"];
_textAdded = YES;
}
...
}
#end
In fact, it looks like you were going down that road with your count instance variable, which is just as good.
Move your code from viewWillAppear: to viewDidLoad:. As viewDidLoad: method is called just once when the view is created.
Related
I created a singleton in ios7 like this:
SharedData.h
#interface SharedData : NSObject
{
}
+ (id)sharedInstance;
#property (strong, nonatomic) NSMutableArray *list;
#end
SharedData.m
#import "SharedData.h"
#implementation SharedData
#synthesize list;
// Get the shared instance thread safe
+ (SharedData *)sharedInstance {
static dispatch_once_t once = 0;
static SharedData *sharedInstance = nil;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
//initialize
list = [[NSMutableArray alloc] init];
}
return self;
}
#end
I always use this code to access this class:
SharedData *sharedData = [SharedData sharedInstance];
The problem is now when I switch the view in my viewDidLoad method the list is empty but in my viewDidAppear method everything is fine. Any ideas?
EDIT:
This is the code how I change the views:
SharedData *sharedData = [SharedData sharedInstance];
//clear feed and add new feed
[sharedData.list removeAllObjects];
[sharedData.list addObjectsFromArray:newList];
//show new gui
[self.navigationController performSegueWithIdentifier:#"goToMain" sender:self];
NOTE: I push from a normal ViewController to a TabBarController -> NavigationController -> TableViewController to display the list.
I guess you have the confusion between these two viewcontroller methods:
-(void)viewDidLoad{
//
}
&
-(void) viewDidAppear{
//
}
viewDidAppear is the method which is called each time your view changes but viewDidLoad is the method which is not necessarily called each time your view changes.
ViewDidLoad method is called when view loads for the first time, after that it doesn't get called until the views are removed/released.
P.S: I suggest you to put the breakpoint in your viewDidLoad and viewDidAppear method and feel it. Your answer lies there.
Hope this helps you alot.
Good Luck.
The problem was i created a segue which went from the button to the next view. Because of this the viewDidLoad gets earlier called than the list assigned. I just changed the segue to go from view to view.
How are you changing from one viewController to the other? Wich classes are the parents of your destination ViewController?,
If you are modifying properties of the view in the prepareForSegue method... you are forcing the view to load.
For example, you are setting the list of your singleton in prepareForSegue, but before setting the list you are modifying a property of your destination viewController. (doing something like destVC.view = XXX or destVC.viewControllers = XX if you are subclassing a UITabBarViewController...) Then you are triggering the viewDidLoad method , and it's executing before you have set the list to the correct value.
Or maybe you are seguing in two different places to the destinationViewController. And when the viewDidLoad happens, you still have not updated the list on the singleton.
Here is the transcription of the chat with the poster of the question: https://chat.stackoverflow.com/transcript/55218
I'm new to IOS dev and am making simple programs this one is a hangman game.
I wanted to pick a random string from a plist file (completed).
I now want to compare the user input text (from a text field) and compare it to the string we have randomly picked from our plist.
Here is my code for MainViewController.m as it is a utility. Only the MainView is being used currently.
#import "MainViewController.h"
#import "WordListLoad.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize textField=_textField;
#synthesize button=_button;
#synthesize correct=_correct;
#synthesize UsedLetters=_UsedLetters;
#synthesize newgame=_newgame;
- (IBAction)newg:(id)sender
{
[self start];
}
- (void)start
{
NSMutableArray *swords = [[NSMutableArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"swords" ofType:#"plist"]];
NSLog(#"%#", swords);
NSInteger randomIndex = arc4random() % [swords count];
NSString *randomString = [swords objectAtIndex:randomIndex];
NSLog(#"%#", randomString);
}
This is where i would like to implement the checking
I have tried characterAtIndex and I can't seem to get it to work for hard coded placed in the string let along using a for statement to systematic check the string.
- (void)check: (NSString *) randomString;
{
//NSLogs to check if the values are being sent
NSLog(#"2 %#", self.textField.text);
}
- (IBAction)go:(id)sender
{
[self.textField resignFirstResponder];
NSLog(#"1 %#", self.textField.text);
[self check:(NSString *) self.textField];
_textField.text = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self start];
}
To compare 2 strings: [string1 equalsToString:string2]. This will return true if string1 is equal to string2. To get the string contained in a UITextfield: textfield.text.
Given that it's a hangman game, I assume you are trying to see if a single letter is contained by a given string - so equalsToString: wouldn't be what you want.
Instead, probably better to use rangeOfString:options:
if ([randomString rangeOfString:self.textfield.text options:NSCaseInsensitiveSearch].location != NSNotFound){
// Do stuff for when the letter was found
}
else {
// Do stuff for when the letter wasn't found
}
Also, as was pointed out by Patrick Goley, you need to make sure you're using the textfield.text value to get the string from it. Same with storing the initial word you'll be using as the hidden word.
There are also a couple of other minor code issues (semicolon in the function header, for example) that you'll need to clean up to have a functioning app.
Edit: Made the range of string call actually use the textfield's text, and do so case-insensitive (to prevent false returns when a user puts a capital letter when the word is lower case, or vice-versa). Also included link to documentation of NSString's rangeOfString:options:
For your check method you are sending the UITextfield itself, instead of its text string. Instead try:
[self check: self.textfield.text];
You'll also need to create an NSString property to save your random string from the plist, so you can later access to compare to the textfield string like so:
declare in the interface of the class:
#property (nonatomic,strong) NSString* randomString;
in the start method:
self.randomString = [swords objectAtIndex:randomIndex];
in the check method:
return [self.randomString isEqualToString:randomString];
My class "TypographicNumberLabel" is a subclass of UILabel. This class overrides the "text" setters and getters of UILabel with the purpose to produce nicely rendered numbers in a table. For instance, it can add some extra white space for right alignment, unary plus signs, append units, etc.
My problem is that this class has worked perfectly fine up to iOS 5.1, but in iOS 6, it has stopped working: It is now rendering exactly as the standard UILabel (but when its properties are accessed from code, they are still giving correct results).
Since this class is used in a huge mass of legacy code, I would really like to repair my original code instead of rewriting it using completely new methods. So, please focus your answers on explaining how to override "-text" and "-setText:" for UILabel in iOS 6.
This is (a simplified version of) my code:
#interface TypographicNumberLabel : UILabel {
NSString *numberText;
}
// PROPERTIES
// "text" will be used to set and retrieve the number string in its original version.
// integerValue, doubleValue, etc. will work as expected on the string.
// The property "text" is declared in UILabel, but overridden here!
// "typographicText" will be used to retrieve the string exactly as it is rendered in the view.
// integerValue, doubleValue, etc. WILL NOT WORK on this string.
#property (nonatomic, readonly) NSString* typographicText;
#end
#implementation TypographicNumberLabel
- (void) renderTypographicText
{
NSString *renderedString = nil;
if (numberText)
{
// Simplified example!
// (Actual code is much longer.)
NSString *fillCharacter = #"\u2007"; // = "Figure space" character
renderedString = [fillCharacter stringByAppendingString: numberText];
}
// Save the typographic version of the string in the "text" property of the superclass (UILabel)
// (Can be retreived by the user through the "typographicText" property.)
super.text = renderedString;
}
#pragma mark - Overridden UILabel accessor methods
- (NSString *) text
{
return numberText;
}
- (void) setText:(NSString *) newText
{
if (numberText != newText)
{
NSString *oldText = numberText;
numberText = [newText copy];
[oldText release];
}
[self renderTypographicText];
}
#pragma mark - TypographicNumberLabel accessor methods
- (NSString *) typographicText
{
return super.text;
}
#end
Example of use (aLabel is loaded from .xib file):
#property(nonatomic, retain) IBOutlet TypographicNumberLabel *aLabel;
self.aLabel.text = #"12";
int interpretedNumber = [self.aLabel.text intValue];
This type of code works perfectly fine in both iOS 5.1 and in iOS 6, but the rendering on screen is wrong in iOS 6! There, TypographicNumberLabel works just like a UILabel. The "figure space" character will not be added.
The issue is at
- (NSString *) text
{
return numberText;
}
You can see the method ([self text]) is called internally, so it's better to return the text you want to be shown, otherwise you can easily ruin internal control logic:
- (NSString *) text
{
return [super text];
}
After having submitted my question, I found a solution myself. Perhaps not the definite solution, but at least a useful workaround. Apparently, the rendering logic of UILabel has changed when attributedText was introduced in iOS 6. I found that setting the attributedText property instead of super.text will work.
To be more specific:
The following line in renderTypographicText
super.text = renderedString;
should be replaced with
if (renderedString && [UILabel instancesRespondToSelector: #selector(setAttributedText:)])
super.attributedText = [[[NSAttributedString alloc] initWithString: renderedString] autorelease];
else
super.text = renderedString;
then the rendering works fine again!
This is a bit "hackish", I admit, but it saved me from rewriting a huge amount of legacy code.
I am new to IOS programming. So far I have been programming in android. So in android when pressing a button code for passing an argument would be like that:
Intent i = new Intent(MainScreen.this,OtherScreen.class);
Bundle b = new Bundle();
b.putString("data_1",data);
i.putExtras(b);
startActivity(i);
and on the activity that opens, i would write something like this:
Bundle b = getIntent().getExtras();
ski_center=b.getString("data_1");
what methods should I need to change in MainScreen and in OtherScreen in IOS to achieve the above.
Basically I will have 3 buttons lets say in my MainScreen and each of it will open the Otherview but each time a different parameter will be passed.
Foe example for each button i have code like these in MainScreen.m
#synthesize fl;
-(IBAction) ifl:(id) sender {
}
So I need your help in where to place the "missing" code, too.
Declare an iVar for your UIViewController ( Android's Activity) like a property in Java.
In MainViewController.m
OtherUIViewController * contr = [[OtherUIViewController alloc] initWithNibname...];
contr.data = yourData;
Edited: added full code...
Intent i = new Intent(MainScreen.this,OtherScreen.class);
Bundle b = new Bundle();
b.putString("data_1",data);
here the MainScreen is the calling code, now in iOS it will be the MainUIViewcontroller
create a OtherUIViewController like this:
OtherUIViewController.h
#interface OtherUIViewController : UIViewController
{
NSData* data;
}
#property (strong, nonatomic) NSData* data;
in the OtherUIViewController.m
#implementation OtherUIViewController.m
#synthetize data;
// override
- (void)viewDidLoad
{
[super viewDidLoad];
// do something with data here
}
to have the 3 different behaviors, the data can be an int, or an NSString.
And in the - (void)viewDidLoad you will check the data value and do 3 diff things.
I hope it helps
I'm using Reachability in my iPad app and discovered some issues when using modalViewControllers.
In my mainViewController I have a BOOL variable determining weather I'm online or not. Here's my code:
// mainViewController.h
BOOL online;
// mainViewController.m
- (void)reachabilityChanged:(NSNotification *)note
{
if([[note object] isReachable]) {
online = YES;
}
else {
online = NO;
}
}
- (void)getOnline
{
NSLog(#"%d", online);
}
// modalViewController.m
#import "mainViewController.h"
- (IBAction)dismissMe
{
mainViewController *main = [[mainViewController alloc] init];
[main getOnline];
[self dismissModalViewControllerAnimated:YES];
}
When I'm calling [self getOnline] within the mainViewController, it returns 1 ('cause I am online).
But: when I'm calling [main getOnline] within the modalViewController, it returns 0 in the log.
Does anybody know why?!
I also tried to put the online variable as a #property into the modalViewController to handle the if online stuff within the modal. But when I assign a value to it (from the main), and log it within the modal, it always returns (NULL).
Hope, you can help me! With best regards, Julian
Short answer: because they use different instances of the online variable.
Long answer: you should only declare BOOL online in the header, not define it. Defining should happen in the .m file, like this:
In the mainViewController.h:
extern BOOL online; // Declare the variable
In the mainViewController.m:
BOOL online; // Define the variable
// the rest of your code
The way your code is written, a separate BOOL online is created for each .m file that includes mainViewController.h; I am sure this is not what you intended.