Setting UILabel text is not working - ios

Here is my .h file
#import <UIKit/UIKit.h>
#interface PersonViewController : UIViewController
#property(strong,nonatomic) NSString *personTitle;
And here is my .m file
#interface PersonViewController ()
#property (weak, nonatomic) IBOutlet UILabel *titleView;
#end
#implementation PersonViewController
//stuff …
-(void)setPersonTitle:(NSString *)personTitle
{
[self.titleView setText:personTitle];// also self.titleView.text=personTitle
[self.titleView setNeedsDisplay];
NSLog(#"The title shoud match as %# :: %#",personTitle,self.titleView.text);
}
-(NSString *)personTitle
{
return self.titleView.text;
}
//… more stuff
#end
The logging shows that the value is (null) for self.titleView.text whereas personTitle prints the appropriate value.
I remember doing this same thing a number of times and it worked. Any ideas why it’s failing this time?
update I use storyboard to set my scenes. And I am using xcode-5 and iOS-7
update: how I call
The user clicks a button, leading to a push segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"enter prepare for segue.");
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:the_identifier_for_person]) {
NSLog(#"segue to person is progressing“);
if ([segue.destinationViewController isKindOfClass:[PersonViewController class]]) {
NSLog(#"segue to person destination is a match");
PersonViewController *aPerson = (PersonViewController *)segue.destinationViewController;
aPerson.personTitle=((MyItem*)self.allItems[indexPath.row]).title;
NSLog(#"segue to person is done");
}
}
}

This sounds like you forgot to wire up your UILabel in the storyboard. Can you confirm that self.titleView is not null?

View controllers create their views on demand, but can spot that only via a call to view. When the view is loaded, your outlets will be populated.
Either call view to force loading or keep the string in abeyance until you get viewDidLoad.
(aside: prior to iOS 6, views would also be released in low-memory situations so the idiomatic thing is to store the string and populate on viewDidLoad)

Having accepted another answer, I wanted to show the pattern that I actually used to solve the problem, in case someone else comes looking. This pattern is best practice (yes, I forgot it for a long moment there).
#pragma mark - update UI
-(void)setPersonTitle:(NSString *)personTitle
{
_personTitle=personTitle;
if (self.view.window) [self updateUI];//only if I am on screen; or defer to viewWillAppear
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateUI];
}
-(void)updateUI
{
self.titleView.text=self.personTitle;
}
It is always important to update the ui when the data has changed, which is why I must make the call inside setPersonTitle. But because the IBOutlets are not yet set when I set personTitle in prepareForSegue, then I must also make the call inside viewWillAppear.

Do you actually call the -(void)setPersonTitle:(NSString *)personTitle method?
It seems that you aren't calling it correctly which would result in the title being null.
After reviewing the prepareForSeque it is clear that you are not calling the method. You are actually just changing the #property named personTitle.
In the viewDidLoad you should have it so that self.titleView.text = self.personTitle;

Related

Using the same view controller multiple times

I'm new to Objective-C and have a question. Did the search multiple times but I couldn't find what I was looking for.
I'm using storyboard for this app. On the homescreen you've got some buttons with labels above them. Those labels should tell a number. When pushing the button you go to a new viewController where you have input that (after 'save') goes back to the homescreen and updates the label with the correct number. All that works great for one button and I'm very happy about it.
The problems are:
1. Since I have multiple buttons with labels, I want to use the same viewController to give input over and over again. I tried connecting every button to slide to the viewController under the identifier "AddData", but Xcode doesn't allow the same identifiers twice or more in storyboard. So I would need something else for this. Any idea?
2. Currently I use the following code to bring back the data to the homescreen:
homeScreenViewController
- (IBAction)unwindToHomeScreen:(UIStoryboardSegue *)segue;
{
inputDataViewController *source = [segue sourceViewController];
self.logoOneLabel.text = source.endTotalNumber;
}
inputDataViewController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) {
return;
} else {
if (endTotalLabelNumber > 0) {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
} else if (endTotalLabelNumber == 0 && totalLabelNumber == 0){
self.endTotalNumber = 0;
} else {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
}
}
}
This works great for the one button, but how to use this with multiple? I heard about Delegates to use the same viewController multiple time and get data back to different places, but I just don't get it. Any help?
You shouldn't need delegates.
What you will need is a property on the view controller that handles input to it knows which button it is handling input for.
When you segue to the input controller, set this property, based on which button was pushed. When you unwind back, fetch this property to know which label to modify.
For example, in your input view controller's .h file, add a property like this:
#property (nonatomic,assign) NSInteger handlingTag;
Or something, whatever name makes sense to you.
Now you need to implement your home screen view controller's prepareForSegue:sender:.
Use the sender argument to determine which button was pushed, and based on that, set the input view controller's new handlingTag property based on the button in a way that you will know what to do with it when we unwind.
Now in the unwind method:
switch (source.handlingTag)
Create a switch structure based on the source's handlingTag property, and set the appropriate label based on this value.
As Jeff points out in the comments, it'd be a really good idea to define an NS_ENUM to use here for the property rather than an NSInteger. The NS_ENUM would allow you to name the values you're using.
There is a few different way to implement what you need. But i think most common its a delegate.
This is how your inputDataViewController looks like:
#import <UIKit/UIKit.h>
#protocol inputDataDelegate;
#interface inputDataViewController : UIViewController
#property (weak) id<inputDataDelegate> delegate;
#property (strong, nonatomic) NSNumber *buttonTag;
#end
#protocol inputDataDelegate <NSObject>
-(void) inputDataViewControllerDismissed:(id)data;
#end
Then in #implementation, you should in "save" button action, message to you delegate method :
[self inputDataViewControllerDismissed:#{#"buttonTag":buttonTag,#"endTotalNumber":endTotalNumber}
Next in homeScreenViewController connect delegate :
#interface homeScreenViewController : UIViewController<inputDataDelegate>
After that in #implementation:
-(void)inputDataViewControllerDismissed:(id)data
{
// if you use modal
[self dismissViewControllerAnimated:YES completion:nil];
// or if you use push
//[self.navigationController popViewControllerAnimated:YES];
switch (data[#"buttonTag"]) {
case 1:
self.lableWtiTagOne = data[#"endTotalNumber"];
break;
case 2:
self.lableWtiTagTwo = data[#"endTotalNumber"];
break;
// number of cases depend how many buttons you have
}
Also, most important, thing didn't forget send self to our delegate:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"inputDataController"])
{
inputDataViewController *inputCtrl = [segue destinationViewController];
inputCtrl.delegate = self;
inputCtrl.buttonTag = sender.tag
}
}

Thread1 exc_bad_access (code =2)... help me

i've been fixing this problem for a few days. but can't seem to get it..
help me out ..
let me explain my situation. Basically, i have navigation controller that contains table view controller and view controller. and i'm making simple phone book app.
And, i have a directory entry declared in extension class
#interface DetailViewController ()
#property DirectoryEntry *dirEntry;
#end
And, in table view, when you click the button it will transfer some data through segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DetailViewController *detailCV = [segue destinationViewController];
if ([segue.identifier isEqualToString:#"cellToDetail"]) {
[detailCV setDirEntry: [self.pbArray objectAtIndex:[self.tableView indexPathForSelectedRow].row]];
} else {
detailCV.dirEntry = nil;
}
//designate delegate !!!
detailCV.delegate = self;
}
My Problem occurs when it execute detailCV.dirEntry = nil; it will call my setter in viewController. it says EXC_BAD_ACCESS
-(void) setDirEntry:(DirectoryEntry *) dirEntry {
self.dirEntry = dirEntry;
}
Thank you in advance..
It's not an EXC_BAD_ACCESS so much as the OS killing your app for smashing the stack. This method is recursing infinitely:
-(void) setDirEntry:(DirectoryEntry *) dirEntry {
self.dirEntry = dirEntry;
}
Your use of dot notation expands to a setter which should make this more clear.
-(void) setDirEntry:(DirectoryEntry *) dirEntry {
[self setDirEntry:dirEntry];
}
Set the instance variable directly, or let the compiler handle it. Properties in class extensions are automatically synthesized.

Trying to implement a delegate that signals when the back button is pressed in the navigation bar - what am I doing wrong?

I have a main list of articles, and upon clicking one it segues to a reading view controller, and there I keep track of the progress of the user's reading with that view controller having an NSNumber property holding the position. I want to update this position back to the root view controller when they press the back button (so I can show them their progress) but my delegate doesn't seem to be working.
In the reading view's .h file:
#property (nonatomic, weak) id<UpdateProgressDelegate> delegate;
...
#protocol UpdateProgressDelegate <NSObject>
#required
- (void)finishedReadingWithPosition:(NSNumber *)position;
#end
In the .m file:
- (void)viewDidDisappear:(BOOL)animated {
[super viewWillDisappear:YES];
if ([self.delegate respondsToSelector:#selector(finishedReading:)]) {
[self.delegate finishedReadingWithPosition:self.position];
}
}
In my root view (note it does indeed implement the protocol):
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ReadBasicArticleSegue"] || [segue.identifier isEqualToString:#"ReadFullArticleSegue"]) {
ReadingViewController *destination = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
self.rowOfLastSelectedCell = #(indexPath.row);
Article *article = self.articles[[self.rowOfLastSelectedCell intValue]];
// Set ReadingViewController's variables so the selected article can be read
destination.textToRead = [article.body componentsSeparatedByString:#" "];
destination.wordsPerMinute = #(1500);
destination.numberOfWordsShown = #(3);
destination.delegate = self;
}
}
and...
- (void)finishedReadingWithPosition:(NSNumber *)position {
Article *article = [self.articles objectAtIndex:[self.rowOfLastSelectedCell intValue]];
article.position = position;
[self.tableView reloadData];
}
I just don't see what I'm doing wrong. When I press the back button, the root view controller still has a 0% progress indicator.
Here:
- (void)viewDidDisappear:(BOOL)animated {
[super viewWillDisappear:YES];
if ([self.delegate respondsToSelector:#selector(finishedReading:)]) {
[self.delegate finishedReadingWithPosition:self.position];
}
}
viewDidDisappear: should pass the same 'did' method to super, not viewWillDisappear: ..
[super viewDidDisappear:animated];
The selector finishedReading: is not the same as the selector finishedReadingWithPosition:. As it is not implemented in the delegate, the conditional is not called.
Typos - or the solution...?
two quick things:
first, in your delegate respondstoSelector check, make sure you are testing for the correct method. You have "finishedReading:" in the check, then you call finishedREadingWithPosition:" in the method call. My guess is that it's skipping that line because its the wrong selector your checking for.
next make sure self.position have a value
and also:
I agree on the [super] call that #He Was mentioned - needs to be a call for the same method

Update UIViewController after Dismissing Modal Segue

I am currently designing the structure for my first iPhone game and ran into a problem. Currently, I have a 'MenuViewController' that allows you to pick the level to play and a 'LevelViewController' where the level is played.
A UIButton on the 'MenuViewController' triggers a modal segue to the 'LevelViewController'.
A UIButton on the 'LevelViewController' triggers the following method to return to the 'MenuViewController':
-(IBAction)back:(id)sender //complete
{
[self dismissModalViewControllerAnimated:YES];
}
The problem is, I have a UILabel on the menu page that prints the number of total points a player has. Whenever I go back to the menu from the level, I want this label to automatically update. Currently, the label is defined programmatically in the 'MenuViewController':
-(void)viewDidLoad {
[super viewDidLoad];
CGRect pointsFrame = CGRectMake(100,45,120,20);
UILabel *pointsLabel = [[UILabel alloc] initWithFrame:pointsFrame];
[pointsLabel setText:[NSString stringWithFormat:#"Points: %i", self.playerPoints]];
[self.pointsLabel setTag:-100]; //pointsLabel tag is -100 for id purposes
}
self.playerPoints is an integer property of MenuViewController
Is there a way I could update the label? Thanks ahead of time!
This is a perfect case for delegation. When the LevelViewController is done, it needs to fire off a delegate method which is handled in the MenuViewController. This delegate method should dismiss the modal VC and then do whatever else you need it to do. The presenting VC should normally handled the dismissal of modal views it presents.
Here is a basic example of how to implement this:
LevelViewController.h (Above the Interface declaration):
#protocol LevelViewControllerDelegate
-(void)finishedDoingMyThing:(NSString *)labelString;
#end
Same file inside ivar section:
__unsafe_unretained id <LevelViewControllerDelegate> _delegate;
Same File below ivar section:
#property (nonatomic, assign) id <LevelViewControllerDelegate> delegate;
In LevelViewController.m file:
#synthesize delegate = _delegate;
Now in the MenuViewController.h, #import "LevelViewController.h" and declare yourself as a delegate for the LevelViewControllerDelegate:
#interface MenuViewController : UIViewController <LevelViewControllerDelegate>
Now inside MenuViewController.m implement the delegate method:
-(void)finishedDoingMyThing:(NSString *)labelString {
[self dismissModalViewControllerAnimated:YES];
self.pointsLabel.text = labelString;
}
And then make sure to set yourself as the delegate for the LevelViewController before presenting the modal VC:
lvc.delegate = self; // Or whatever you have called your instance of LevelViewController
Lastly, when you are done with what you need to do inside the LevelViewController just call this:
[_delegate finishedDoingMyThing:#"MyStringToPassBack"];
If this doesn't make sense, holler and I can try to help you understand.
Make a property self.pointsLabel that points to the UILabel, then you can just call something like [self.pointsLabel setText:[NSString stringWithFormat:#"Points: %i", self.playerPoints]]; to update the label with the new score
In your modal view header file, add the property:
#property (nonatomic,assign) BOOL updated;
Then in your main view controller, use didViewAppear with something like:
-(void)viewDidAppear:(BOOL)animated{
if (modalView.updated == YES) {
// Do stuff
modalView.updated = NO;
}
}
Where "modalView" is the name of that UIViewController that you probably alloc/init there.
Add more properties if you want to pass more info, like what level the user picked.

Delegate for UITextField not working...Return button not responding

I've just started with xcode and objective-c and did some very basic apps, but what i'm having problem with is very basic this. the keyboard return button not hiding the keyboard.
I've searched the internet for the solution and all they say is to connect delegate to the file's owner and add the function and it should work, i did that and nothing is working.
I have an ok button and it is working and also clicking on any free space on the screen is working, just the return button....
I am using the simulator, not testing on iphone yet. (xcode 3.2.5 64 bit with the 4.2 simulator).
This is the line of code that should connect the delegate to every textFiled.
1. i've tried already to return both YES and NO, didn't work.
2. i've tried both a specific object name for the textField and this general way, didn't work.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
In the: basic view controller connection -> connections -> outlets, i have the: delegate -- File's Owner. and in the file's owner in referencing outlets there is: delegate - Round style text.....
EDIT - i forgot to mention before, i've check and the method isn't being called!!!
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
NSLog(#"Working!!!");
[textField resignFirstResponder];
return YES;
}
what should i do to make it happen? that is why people say to connect the delegate, but in my case it is connected and not triggering the function...i know it is kind of dumb question but for a nobie like me the solution is not obvious...
OK, another Edit - with all my code: just can't understand what to do....
This is: basicViewController.h:
#import <UIKit/UIKit.h>
#interface basicViewController : <#superclass#> <UITextFieldDelegate>
#interface basicViewController : UIViewController <UITextFieldDelegate> {
//every object that we want to interact with (like text field or lable) is call an outlet!!!!
//here we define the outlets for our program
IBOutlet UITextField *txtName;
IBOutlet UILabel *lblMessage;
}
//here are the getters and setter for our outlets
#property (nonatomic, retain) IBOutlet UITextField *txtName;
#property (nonatomic, retain) IBOutlet UILabel *lblMessage;
//method decleration for the OK button action
- (IBAction) doSomething;
//method for hiding the keyboard when clicking on empty area in the app
//we will put an invisible button on all area and clicking on it will make keyboard disapear
- (IBAction) makeKeyboardGoAway;
#end
This is basicViewController.m:
#import "basicViewController.h"
#implementation basicViewController
//synthesizeing the objects that we made' this will create the getter and setters automaticly
#synthesize txtName;
#synthesize lblMessage;
- (IBAction) doSomething{
// makeing keyboard disapear when pressing ok button (doing that form the text field)
//when pressing the OK button, the keyboard will disapear and when clicking in the text field it will show again
[txtName resignFirstResponder];
NSString *msg = [[NSString alloc] initWithFormat:#"Hello, %#",txtName.text];
//the objective-c way for setting the test in the text field
[lblMessage setText:msg];
//the regular object oriented way
//lblMessage.text = msg;
[msg release];
}
- (IBAction) makeKeyboardGoAway{
[txtName resignFirstResponder];
}
//when clicking the return button in the keybaord
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
NSLog(#"Working!!!");
[textField resignFirstResponder];
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Maybe now i am more clear, sorry i didn't do it before.
Any one has an idea what am i doing wrong? it should be pretty strait forward.....
EDIT - Adding an image of all the elements, i hope that will help to help me :-)
10x a lot for every one that is trying to help....i really like this framework, it is so great after c++ and java, python and many other...and i am working with a book, but it is for ios 3.1, maybe that is the problem.....
Firstly you should check if textFieldShouldReturn: is actually being called by adding an NSLog statement or breakpoint at the beginning of the method.
Once that's out of the way, try an manually declare that your view controller conforms to <UITextFieldDelegate> protocol in your interface file:
#interface YourClass : ... <UITextFieldDelegate>
Also declare a property & outlet for your UITextField, make the appropriate connections in IB and manually declare self as the UITextField delegate with:
self.yourUITextFieldObject.delegate = self;
Once that's done see if your method above is now being called and make sure you return YES.
Just write one line in the
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
}
before return YES;
the final version will be as given below:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
NSLog(#"%#",textField.text);
}
You need to assign the delegate of the textfields to your file owner. The textfields are sending the message, but doesn't have a delegate to respond to it.
Use the interface builder to do that.
You have to implement this method..
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
Like Rog said, don't forget to register the textfield to the delegate, you can do this manually as he said but in Storyboard you can just control drag from all of your textfields to the view controller and register the delegate (choose delegate). Only the textfields that are registered can make use of all those methods.
So this line is important:
self.yourUITextFieldObject.delegate = self;
Or even more easy these days is to just use the storyboard:
textfield is in a subview? in this case, make sure textfield have as delegate the FileOwner.
put a log at the
- (IBAction) makeKeyboardGoAway
function. I think its this method everytime anything is tapped on the screen. In that case, you will need to send the touch event to the text field. Not sure how this is done but that should do it.
Else try removing the which takes care of tap(click) all over the view and try to do what you are doing.
Most likely the problem is that your actual view controller in the running application is not a "basicViewController" but a UIViewController that does not implement the UITextFieldDelegate-protocol.
What you've done in the interface builder by selecting your class "basicViewController" as the FilesOwner is just declaring the FilesOwner-object in your running application to be of type basicViewController; the actual object is not instantiated by this declaration and in your case it is not in the xib / nib.
Some other part of your code actually instantiates a view controller object and loads the xib / nib file. At that place, I guess your code is instantiating a UIViewController (typically by auto-generated code) and not an instance of your basicViewController; you simply have to change the class there.
Furthermore, this error often happens when using a UINavigationController or UITabBarController in the Interface Builder that is (should be) configured to instantiate and load other custom views. If you use such a higher-level controller, double-check that it is actually configured to use your basicViewController, not UIViewController when loading your view from the xib / nib.
Hope, that solves the issue!
Can you try this..
#interface ClassName : SuperClass < UITextFieldDelegate >
Use like this...
textfield.delegate=self;
and use the UITextFieldDelegate in .h class
You can always dismiss the keyboard when you don’t even know which view the text field is in by using:
Objective-C:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder)
to:nil
from:nil
forEvent:nil];
Swift:
UIApplication.sharedApplication().sendAction("resignFirstResponder",
to:nil,
from:nil,
forEvent:nil)

Resources