Is there a simple way for checking if the setImage method of a UIButton has been called? I use storyboard to place my button (not that this should matter in this case). Basically I (i.e. another part of my code) would like to be notified my UIButton catImage's setImage has been called.
THere are many ways to notify another object when a particular event occurs in iOS.
Use delegate, when you set the image programatically call a delegate method in the other object to notify that event.
Send NSNotification, when the image is set send out a NSNotification. The other object should observe for this notification.
[[NSNotificationCenter defaultCenter] postNotificationName:#"Imageset" object:self userInfo:nil];
Use key value observing. So that if the value of image changes, the other object get notification.
For example:
-(void)setImageWithImageView:(UIImageView *)img withSuccessBlock:(void(^)(UIImageView *newImg))success {
UIImage *theImage = [UIImage imageNamed:#"prew_01.png"];
img = [[UIImageView alloc]initWithImage:theImage];
if (img != nil) {
success(img);
} else {
DLog(#"failure");
}
}
You can simply change some part of source and add your alternative...
Related
I'm using the NSNotification centre to detect changes of currency so I can update all the other classes. When a currency change occurs, all the other classes and views get updated, however when there is no currency change, and if you press the back button to go back to the home page the view loads on top of the already existing view.
Code for NSNotification center
if ([overviewModel.currency isEqual:#"GBP"]){
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:self];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:self];
}
Code for handling updated data in homepage:
for (UIView *b in self.view.subviews) {
[b removeFromSuperview];
}
self.build = [[ApiRequestBuild alloc]initWithVersionKey:kAPI_VERSION_KEY requestType:kAPI_REQUEST_TYPE data:#""];
[self.build setQueryWithSection:#"homepage" value:#"" parameter:#[]];
self.request = [[ApiRequest alloc]init];
self.request.delegate = self;
[self.request sendRequestWithParams:[self.build buildConfig] toUrl:kAPI_URL_STRING];
I know why this is happening, the request gets sent again so the page loads on top of the already existing page, what I don't understand is why doesn't the remove from subview code get rid of the of the view and how would I be able to fix this? thanks
The removeFromSuperview won't work if it's being called from another thread (than main thread). Your notification will be received on the same thread it was fired from. I'll wager that you're listening to a model change event (regarding your currency state) on another thread.
Try dispatching to main queue before walking your copy of subviews to remove them all.
I have a UIButton with a titleLabel, for example, like this: #"Download it!"
I would like after my download is finished, update the titleLabel of my button with another text, for example, like this: #"Already downloaded!"
I can change the state (enable or not) but impossible to refresh / update the titleLabel of the UIButton.
Any idea how to do this ? I tried [myButton setNeedsDisplay]; but it doesn't work.
Thank for your suggestions and help.
UPDATE 1:
Solution:
[yourButton setTitle:<#(NSString *)#> forState:<#(UIControlState)#>]
Have you tried this?
[yourButton setTitle:<#(NSString *)#> forState:<#(UIControlState)#>]
You can change the text in a button's title label.
[aButton setTitle:#"Already downloaded!" forState:UIControlStateNormal];
For more information on the topic, and the full list of control states, see this: https://developer.apple.com/library/ios/documentation/uikit/reference/uicontrol_class/reference/reference.html#//apple_ref/doc/c_ref/UIControlState
All the example explained with this post explains the change of title on button's various state like UIControlStateNormal, UIControlStateHighlightedbut it is not doing it on download complete.
The simplest way is to keep notify your viewController that the some process (the download) is finished. Then you change the button Title as required.
May be try this code.
Add a button & a Notification observer in your ViewController viewDidLoad as
self.someButton.title = #"Download Now"; // set the button title
// Add notification Observer
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notifyDownloadComplete:)
name:#"DOWNLOAD_COMPLETE"
object:nil];
Now define the target method of the Observer to perform the Button title Change as
-(void)notifyDownloadComplete:(NSNotification*)note {
self.someButton.title = #"Already Downloaded";
}
Now add a Download Method via GCD & then post the notification once it is completed.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Here your non-main thread. Try Downloading something
dispatch_async(dispatch_get_main_queue(), ^{
//Here you returns to main thread.
[[NSNotificationCenter defaultCenter] postNotificationName:#"DOWNLOAD_COMPLETE"
object:nil];
});
});
This will change the title of self.someButton to whatever you want, as in this case as Already Downloaded.
Hope that helps.
I need to update an image on the main view controller from a pop up view controller.
The button is called 'Feature2btn' on the Main View (EraViewController) but when I try the following code on the popup view controller it won't work.
It needs to be an immediate update as the main view is still showing in the background and does not reload so the change needs to be directly caused by the action on the pop up view.
- (IBAction)purchase:(id)sender {
HomeController = [[EraViewController alloc] initWithNibName:#"EraViewController" bundle:nil];
UIImage *image = [UIImage imageNamed:#"ico_plan.png"];
[(EraViewController*)HomeController setFeature2Btn:[feature2Btn setImage:[UIImage imageNamed:#"image.png"] forState:UIControlStateNormal];
}
There is (at least) two ways to do this:
You use a notification that one controller listens to and the other sends at the appropriate time.
You create a delegate protocol that the first controller implements and the second on calls.
The delegate one is a bit more complicated but generally considered good style. The notification one is not bad, either, but slightly less "elegant".
I will describe the notification based one here, because it seems ok for your case and would also allow to react to the purchase in multiple places by just registering for the notification there, too.
In the controller that has the image to be updated, register for a notification in viewDidAppear::
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateImage:) name:#"UpdateImageNotification" object:nil];
Implement the updateImage: method:
-(void)updateImage:(NSNotification*)note
{
NSString* newImageName = note.userInfo[#"imageFileKey"];
// ... update UI with the new image
}
Also make sure to deregister for that notification when the view goes away:
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super viewWillDisappear:animated];
}
In the other controller, that triggers the update, fire the notification at the appropriate place:
-(IBAction)purchase:(id)sender
{
// ...
NSDictionary* userInfo = #{#"imageFileKey" : newImageName};
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UpdateImageNotification"
object:self userInfo:userInfo];
// ...
}
The object parameter in the notification context is to be used to specify if you want to listen to the notifications by any object or just by a very specific instance. In many cases the actual instance is not relevant, but you just discern the notifications by their name (like "UpdateImageNotification" in this case).
The userInfo dictionary is intended to carry along any information you need to provide with the notification. That's why I introduced a key "imageFileKey" that is associated with the new image name.
I think you made a small mistake. But before going ahead I just want to confirm whether following is the scenario. Correct me if I am wrong
1. You have mainViewController/HomeViewController (of type EraViewController), where you want to update the image
2. On mainViewController you have a popup screen, in which above code is written. The code is intended to change the button image on mainViewController/HomeViewController
If above is the scenario then I suggest following solution.
ERROR YOU MADE
You are creating a new object of EraViewController in the code you posted above and changing the image. According to OOPS concepts, new instance of that controller will be created(a second instance which is not visible on the screen) and you are applying new image in that instance. Now as that instance is not at all visible, you get a feeling that screen is not updating.
SOLUTION
There are at-least 3 solution to this problem
1. One solution will be Daniel Schneller gave in answer (with little bit modifications probably)
2. To achieve this through the delegates.
- You have to write the protocol in the PopViewController and have to implement that in the HomeViewController/mainViewController.
- As the image changes, in PopViewController, That has to be notified to the mainViewController, using delegate and protocol method.
- So that the mainViewController will get the notification and a protocol method will be executed. In that method you should have a code to update the image on the button.
3. (This is not suggested as this is not a good design)You have maintain the instance of the actual viewController which is visible on the screen (in a variable, lets say homeViewController). Then you can use following code in popupViewController
- (IBAction)purchase:(id)sender {
UIImage *image = [UIImage imageNamed:#"ico_plan.png"];
[(EraViewController*)homeViewController setFeature2Btn:[feature2Btn setImage:[UIImage imageNamed:#"image.png"] forState:UIControlStateNormal];
}
Hoep this helps.
Thanks
Try this,
- (IBAction)purchase:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateimage" object:imageName];
}
in mainviewcontroller
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateiimage:) name:#"updateimage" object:nil];
}
- (void) updateiimage:(NSNotification *)notification
{
NSString * text =[notification object];
//update Image
}
How can I get a CCMenu item to call a selector once unselected?
Is this even possible?
when adding a CCMenuItem I can specify as selector a function which will be called on button activation. What I would like is to be able to call a function once a button is not "selected" anymore (I mean selected and not activated).
I guess I have to override some methods but not sure which one :).
Any suggestion?
Subclass CCMenuItemWhatever and override one method (derived from CCMenuItem) :
-(void) unselected{
[super unselected];
[self myUnselectedExtension];
}
-(void) myUnselectedExtension{
// whatever is your need , do it here.
}
It's not possible without modifying CCMenu.
You can however run a scheduled update and check each selected property of the menu items, and observe any changes from the previous selected status (stored in an ivar for example).
Currently, I'm downloading data from a web server by calling a method fetchProducts. This is done in another separate thread. As I successfully download fifty items inside the method stated above, I post a notification to the [NSNotification defaultCenter] through the method call postNotificationName: object: which is being listened to by the Observer. Take note that this Observer is another ViewController with the selector updateProductsBeingDownloadedCount:. Now as the Observer gets the notification, I set the property of my progressView and a label that tells the progress. Below is the code I do this change in UI.
dispatch_async(dispatch_get_main_queue(), ^{
if ([notif.name isEqualToString:#"DownloadingProducts"]) {
[self.progressBar setProgress:self.progress animated:YES];
NSLog(#"SetupStore: progress bar value is %.0f", self.progressBar.progress);
self.progressLabel.text = [NSString stringWithFormat:#"Downloading %.0f%% done...", self.progress * 100];
NSLog(#"SetupStore: progress label value is %#", self.progressLabel.text);
[self.view reloadInputViews];
}
});
The idea is to move the progressView simultaneously as more items were being downloaded until it is finished. In my case, the progressView's animation will just start right after the items were already downloaded, hence a delay. Kindly enlighten me on this.