问题
I have an NSTimer
that runs every 10 seconds and is kicked off from LandingController.m. It continues to run as you go to other views in the application. I want to be able to (when a certain condition is met within that timer) update a label field from another view GuardMenu.m The label I want to update is called CurrentZone.text and I want to update it from value "N" to value "Y."
Here's my timer on LandingController.m
self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:@selector(checkForMessages)
userInfo:nil
repeats:YES];
Which calls this on LandingController.m
- (void)checkForMessages
{
if ( //some condition here ){
//update CurrentZone.text label in GuardMenu view and set equal to "Y"
} else {
//no need to update label in GuardMenu as it's currently equal to "N"
}
}
回答1:
First create a NSNotification in your init
method of GuardMenu class
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification:) name:@"TextChangeNotification" object:nil];
}
return self;
}
Then implement the notification's selector, this is where you will be changing your CurrentZone
label text.
- (void)receiveNotification:(NSNotification *) notification {
if ([[notification name] isEqualToString:@"TextChangeNotification"]) {
NSLog (@"Change you label here");
self.lblCurrentZone.text = @"Y";
}
}
Now in your LandingViewController.m
-viewDidLoad
Method
Start the timer.
self->msgTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(checkForMessages) userInfo:nil repeats:YES];
Now implement the @selector
for the NSTimer, this is where you will be sending the notification back to the GuardMenu class
- (void)checkForMessages {
NSString *strText = @"test";
if ([strText isEqualToString:@"test"]){
[[NSNotificationCenter defaultCenter] postNotificationName:@"TextChangeNotification" object:nil];
}
else {
}
}
NOTE: the NotificationName
should be the same.
Sample Project Code Dropbox Link
回答2:
You can use the prepareForSegue
method to pass objects between view controllers in the storyboard. For example, to pass a string from the GreyViewController to the OrangeViewController, in GreyViewController.m you have:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
OrangeViewController *orange = [segue destinationViewController];
orange.self.orangeString = @"text for orangeView";
}
Then in the viewDidLoad
of the other view controller, in the OrangeViewController.m, you can set the text of the label by doing the following:
self.orangeLabel.text = self.orangeString;
回答3:
Maybe you should describe which error you're getting. Is your checkForMessages
method (not) firing? Use an NSLog()
message to check. Otherwise, check if the UILabel
you want to change is actually loaded into memory (i.e. is not nil
). Please also let us know if the currentZone.text
is part of the view hierarchy of the LandingController
or of another view controller.
回答4:
You can make use of notifications.
In GuardMenu class init
register for custom notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveNotification:)
name:@"MessageChangeNotification"
object:nil];
In LandingController->checkForMessages method post the notification when condition is satisfied.
[[NSNotificationCenter defaultCenter] postNotificationName:@"MessageChangeNotification"
object:nil];
In GuardMenu class implement the notification callback selector
- (void) receiveNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:@"MessageChangeNotification"]) {
NSLog (@"Successfully received the notification");
//Change the label text here..
}
}
Hope it helps!
Amar.
回答5:
Make sure that the label you are trying to edit is declared as a property in the appropriate view and properly synthesised. Also make sure it is connected in Interface Builder.
In GuardMenu.h
@property (strong, nonatomic) IBOutlet UILabel *CurrentZone;
Also, in LandingController.h
, import GuardMenu.h
:
#import "GuardMenu.h"
You will now be able to access the label and its text property from LandingController.h
using
-(void)checkForMessages
{
GuardMenu *guardMenu = [[GuardMenu alloc]init];
if (/* some condition here */) {
//update CurrentZone.text label in GuardMenu view and set equal to "Y"
guardMenu.CurrentZone.text = @"Y";
} else {
//no need to update label in GuardMenu as it's currently equal to "N"
}
}
回答6:
For this you should use KVO(Key Value Observing). There are lot of ways to pass notifications, but KVO is potentially much simpler. I suspect that Notification is used more often because you can do a ‘chain of responsibility’ for an event as opposed to just assigning an observer. However, just having an observer in a controller that can watch a particular property in another object and get notified of changes is a powerful and simple way to solve a whole class of problems.
Firstly set a public property in LandingController
like "lablelText
" .
Then add the observer once, when you create the LandingController view. Once you've added the observer, the observeValueForKeyPath:ofObject:change:context: method will be executed in GuardMenu, so you can do the update to the GuardMenu UI from there. You shouldn't need to do anything every time GuardMenu is about to appear.
In GuardMenu, you should probably create LandingController just before you are going to push LandingController onto the controller stack, presumably in the event handler for some action the user took. Immediately after you create LandingController, add the observer in GuardMenu with the correct NSKeyValueObservingOption
value.
If you just want to be notified whenever the public property "lablelText" in LandingController is changed, then try this:
LandingController
@interface LandingController : UIViewController {
}
@property (strong, nonatomic) NSString* lablelText;
- (void)checkForMessages;
@end
@implementation LandingController
@synthesize lablelText;
- (void)checkForMessages
{
if ( //some condition here ){
//update CurrentZone.text label in GuardMenu view and set equal to "Y"
self.lablelText = @"Y";
} else {
//no need to update label in GuardMenu as it's currently equal to "N"
self.lablelText = @"N";
}
}
@end
GuardMenu
@interface GuardMenu : UIViewController {
}
@property (strong, nonatomic) IBOutlet UILabel* nameLabel;
- (IBAction) methodToHandleEvent:(id)sender;
@end
@implementation GuardMenu
- (IBAction) methodToHandleEvent:(id)sender{
LandingController* tempVC = [[LandingController alloc]init];
[tempVC addObserver:self forKeyPath:@"lablelText" options:NSKeyValueObservingOptionNew context:NULL];
[self.navigationController pushViewController:tempVC animated:YES];
}
- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
// Here you will be notified everytime lablelText changes
if ([keyPath isEqual:@"lablelText"]) {
NSString* changedName = [change objectForKey:NSKeyValueChangeNewKey];
// do something with the changedName - call a method or update the UI here
self.nameLabel.text = changedName;
}
}
@end
As an alternative for this you can use NSNotificationCeneter to pass notifications from one class to another for some event. For this you can check my detailed answer How to pass Notifications from one class to another for some event.
Hope it helps you.
回答7:
Create a notification in the init of GuardMenu class
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveNotification:)
name:@"UpdateCurrentZoneNotification"
object:nil];
In the LandingController,
(void)checkForMessages {
if ( //some condition here ){ [[NSNotificationCenter defaultCenter] postNotificationName:@"UpdateCurrentZoneNotification" object:nil]; //update CurrentZone.text label in GuardMenu view and set equal to "Y" } else { //no need to update label in GuardMenu as it's currently equal to "N" }
}
来源:https://stackoverflow.com/questions/16498422/update-text-label-from-another-view