Can't programmatically hide UIButton created with IB - iphone

My iOS UIButton is correctly linked from IB to an IBOutlet in my view controller, as I can change its title from my code. Ie:
[self.myButton setTitle:#"new title" forState:UIControlStateNormal]; //works
However,
[self.myButton setHidden:YES]; //doesn't work
//or
self.myButton.hidden = YES; //doesn't work
What's going on? How can I make myButton disappear?
Update: some additional info
Here's the code related in to my UIButton:
in my .h file
IBOutlet UIButton *myButton;
-(IBAction)pushedMyButton:(id)sender;
#property (nonatomic,retain) UIButton *myButton;
in my .m file
#synthesize myButton;
- (void)pushedMyButton:(id)sender{
self.myButton.hidden = YES;
}
- (void)dealloc{
[self.myButton release];
}

Ok I found a workaround that works but I still don't know why my original code wasn't working in the first place. I used Grand Central Dispatch to dispatch a block containing the hide call on the main queue, like this:
dispatch_async(dispatch_get_main_queue(), ^{
self.myButton.hidden = YES; //works
});
Interesting. None of the initial code in my IBOutlet was wrapped in GCD blocks though. Any ideas?

That should work, try rename it and hide it just to check that there aren't two buttons on top of each other.

I had this same problem and found the solution was to put the hidden in the right place, in my case in the viewDidLoad function.

User Interface (UI) API (UIKit ...) methods have to be run on Main Thread!
So this will run on Main thread (as *dispatch_get_main_queue*):
dispatch_async(dispatch_get_main_queue(), ^{
self.myButton.hidden = YES; //works
});
BUT usually we do something like this:
[self performSelectorOnMainThread:#selector(showButton) withObject:nil waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(hideButton) withObject:nil waitUntilDone:NO];
-(void)showButton
{
myButton.hidden = NO;
}
-(void)hideButton
{
myButton.hidden = YES;
}
As per Apple's documentation: http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html
"
Threading Considerations
Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.
"

What worked for me is putting the manipulating code in viewDidLoad instead of initWithNibName, like this:
- (void)viewDidLoad
{
btnRestart.enabled = false;
}

Had the same problem: button.hidden = YES didn't hide.
Solved it when I defined it in the .h file using #property and #synthesize in the .m file thus making it self.button.
Now self.button.hidden = YES works

Related

How to set activity indicator view in iOS

In .h file:
UIActivityIndicatorView *mySpinner;
In viewDidLoad:
mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
mySpinner.center = CGPointMake(160, 240);
mySpinner.hidesWhenStopped = YES;
[self.view addSubview:mySpinner];
and finally I am calling it on button click as:
[mySpinner startAnimating];
The problem is that when I called it in viewDidLoad it showed and start animating but didn't animate on button click not even show. Help me on this issue.
Thanks in advance
I'm guessing your 'mySpinner" ivar isn't being properly retained or declared.
In your .h file, declare it as a property. That is:
#property (strong) UIActivityIndicatorView *mySpinner;
then, when you create it:
self.mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
and when you reference it in your button click method:
[self.mySpinner startAnimating];
Thanks to everyOne who helped me ….
I have resolved the issue and here i am posting the answer for some one who is in search of this… actually the issue is "i am Downloading some records on synchronous request. So thats why UI Updation is blocked for a while.
so this is creating problem here….
On Button click i just make a new thread that call a method 'start'
[NSThread detachNewThreadSelector: #selector(Start) toTarget:self withObject:nil];
and the 'start' method is
- (void) Start
{
Spinner.hidden = NO;
[mySpinner startAnimating];
}

Overlay View with Subview, don't know calling VC

I need a view (infoView) to be displayed as an overlay on top of another view. As this infoView should be callable from every view of the app(e.g. introView), I'd like the code to be in the infoViews VC and just call its methods when an action at the currentView (introView) happens. I can't use push and pop, as I need to change the background color (infoView) and especially the alpha of the calling view (introView), so right now I do it with insertSubview.
My Code by now:
introVC .h
- (IBAction) openInf:(id)sender;
IBOutlet InfoVC *infoScreenVC;
introVC .m
- (IBAction) openInf:(id)sender {
[infoScreenVC openInfoMethod];}
infoVC .h
- (IBAction) closeInfoPressed;
- (void) openInfoMethod;
- (void) closeInfoMethod;
infoVC .m
- (IBAction) closeInfoPressed {
[self closeInfoPressed];}
- (void) closeInfoMethod {
[self.view removeFromSuperview];
[self.xx.view setAlpha:1.0f];}
- (void) openInfoMethod {
self.view.backgroundColor = [UIColor clearColor];
[self.xx.view setAlpha:0.2f];
[((MyAppAppDelegate *)[UIApplication sharedApplication].delegate).window
insertSubview: self.infoScreenVC.view aboveSubview: self.xx.view];}
When I push the button to show the infoView, my NSLogs tell me the method was called, but I can see the Subview wasn't added. I have absolutely no clue what to insert where right now it says xx in my code, as a VC reference from intro doesn't show me the screen.
If I put that code in introVC an modify it, it does show the infoView, calls the correct method to close, but again can't close (when I'm in introVC). I can't figure out how to tell my app who was the calling VC to get back there.
At some point, when all the code was in introVC I managed to even remove the Subview, but couldn't set the Alpha of introVC back to one.
I do struggle with that since two days.. -.- Or is there maybe an easier solution even?
Thank you very much!
//Edit after sergios answer:
intro.m
- (IBAction) openInf:(id)sender {
introViewController *introVC;
[infoScreenVC openInfoMethod:];}
info.h
- (void) openInfoMethod:(introViewController *introVC);
info.m
- (void) openInfoMethod:(introViewController *introVC) { //error occurs here
self.view.backgroundColor = [UIColor clearColor];
[self.introVC.view setAlpha:0.2f];
[((MyAppAppDelegate *)[UIApplication sharedApplication].delegate).window
insertSubview: self.infoScreenVC.view aboveSubview: self.introVC.view];}
and the occurring error says
Expected ')' before 'introVC'
I'm not sure how to pass the VC reference properly.
Thank you for your help!!
//EDIT Working Code:
As it works now, I'd like to sum things up:
- I give the calling VC (introVC) to openInfoMethod on the Action openInf like [infoVC openInfoMethod:introVC].
In openInfoMethod I "save" the calling VC in a local variable of type introVC (?) and add the overlay etc.
When the Action of the infoViewController named closeInfoPressed occurs, it calls infoViewController's method closeInfoMethod like self closeInfoMethod:introVC.
In that method I remove self.view from Superview and set introVC.view's Alpha to 1 like introVC.view setAlpha:1.0f
So the codesnippets are
intro.h
IBOutlet InfoscreenViewController *infoScreenVC;
#property (nonatomic, retain) IBOutlet InfoscreenViewController *infoScreenVC;
- (IBAction) openInf:(id)sender;
intro.m
#synthesize infoScreenVC;
- (IBAction) openInf:(id)sender {
UIViewController *introVC = self;
[infoScreenVC openInfoMethod:introVC];
}
info.h:
- (void) openInfoMethod:(UIViewController *)rootVC;
- (void) closeInfoMethod:(UIViewController *)callingVC;
info.m
- (void) closeInfoMethod:(UIViewController *)callingVC;{
[self.view removeFromSuperview];
[callingVC.view setAlpha:1.0f];
}
- (IBAction) closeInfoPressed{
[self closeInfoMethod:introVC];
[self.view removeFromSuperview];
}
If your problem is "figure out how to tell my app who was the calling VC to get back there", why don't you add a parameter to openInfo selector, like here:
info.h
- (void) openInfoMethod:(introViewController *)introVC;
info.m
- (void) openInfoMethod:(introViewController *)introVC {
<your implementation here>
}
would this work for you?
EDIT: your code from intro.m has got a small problem,
- (IBAction) openInf:(id)sender {
introViewController *introVC;
[infoScreenVC openInfoMethod:];
}
indeed, you are not initializing your introVC variable, so that when you pass it into -openInfoMethod: it will have some weird value and cause a crash.
As far as I can grasp from your code, intro.m should be the implementation file for your introViewController, therefore you can simply call:
[infoScreenVC openInfoMethod:self];
but please, before doing this, confirm that self is actually your introViewController.
This should be ilke this
(void) openInfoMethod:(introViewController *)introvc you are passing parameters in a wrong way.
If I understand your question correctly, your infoView should be a subclass of UIView and instantiated from any of your view controllers using:
InfoView *infoView = [[InfoView alloc] initWithFrame:CGRectMake(originX,originY,width,height)];
Then you simply add it as a subview of your view controllers view:
[self.view addSubView:infoView];
[infoView release]; // It's safe to release it here as your view controller's view is retaining it
And when you are done with it, simply call
[infoView removeFromSuperview];
As an aside, you could create some simple methods inside infoView that include introducing animation when the view is presented or removed. Here's an example for fade in and out which assume you set the alpha to zero initially when you create the view:
- (void)fadeIn {
[UIView animateWithDuration:1.0f animations:^{self.alpha = 1.0f}];
}
And fade out
- (void)fadeOut {
[UIView animateWithDuration:1.0f animations:^{self.alpha = 0f}
completion:^(BOOL finished){self removeFromSuperView}];
}

Delegate not being called

I've a viewcontroller "ResultsViewController" with a button called emailbutton. when this button is pressed, i want a function to be called from another view called "Illusions_AppViewController" (both these viewcontrollers are not linked).
Therefore i defined a protocol in the "ResultsViewController.h":
#protocol ResultsViewDelegate <NSObject>
#optional
- (void) resultspage;
#end
#interface ResultsViewController : UIViewController
{
id<ResultsViewDelegate> mydelegate;
UIButton *emailButton;
}
#property(nonatomic,retain) IBOutlet UIButton *emailButton;
#property (nonatomic,assign) id<ResultsViewDelegate> mydelegate;
#end
In the ResultsViewController.m :
-(IBAction)emailButtonPressed:(id)sender
{
NSLog(#"entered emailbuttonpressed");// the app enters this method and gets hanged
if ([mydelegate respondsToSelector:#selector(resultspage)]) {
NSLog(#"entered respondstoselector");// this is never displayed in the log-showing that the delegates doesnt respond to selector
[mydelegate resultspage];
}
}
In my other view, "Illusions_AppViewController.m":
- (void)resultspage{
NSLog(#"Entered results page");
ResultsPageController *resultspagecontroller = [[ResultsPageController alloc] initWithNibName:#"ResultsPageController" bundle:nil];
resultspagecontroller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:resultspagecontroller animated:YES];
}
Would appreciate if anyone can help me with this. I've no clue of why the delegate is not called. the app gets hanged as soon as i press the emailbutton. Thanks!
The implementation/use of delegates is wrong. Please refer to this tutorial.
Hope this helps.
Thanks,
Madhup
or is there any other way to get this done. i just need the results page function to be called whenever the email button is pressed. i tried using this way:
ResultsViewController.m
-(IBAction)emailButtonPressed:(id)sender
{
NSLog(#"entered emailbuttonpressed");
illusions_AppViewController *illusionsview = [[illusions_AppViewController alloc]init];
[illusionsview performSelector:#selector(resultspage)];
}
Now the results page function gets called, but the resultspagecontroller that it needs to display as a modalviewcontroller never appears.the app hangs, and no errors in the console either.
To answer your second question, you are on the right track. Simply create an instance of your Illusions_AppViewController and call the illusionsView method in it instead using:
- (IBAction)emailButtonPressed {
illusions_AppViewController *illusionsview = [[illusions_AppViewController alloc]init];
[illusionsview resultspage];
[illusionsview release];
}

accessing variables/functions from subclass in Objective C

I am having a problem with accessing public variable 'activity', which is a UIActivityIndicatorView type, see class declaration below in QuickStartViewController.h:
#interface QuickStartViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
#public
IBOutlet UIActivityIndicatorView *activity;
}
#property (nonatomic, retain) UIActivityIndicatorView *activity;
#end
The function is called from another class:
#import "QuickStartViewController.h"
#interface NumberValidator : QuickStartViewController....
See below:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[activity startAnimating];
NSLog(#"This function is called, but [activity startAnimating] still doesn't work...");
}
Note: [activity startAnimating] works fine when called within the QuickStartViewController class.
Do you have any suggestions as to why [activity startAnimating] is not working?
The IBOutlet macro indicates that the UIActivityIndicatorView will be constructed and assigned when an instance of QuickStartViewController or NumberValidator are instantiated via NSBundle's +loadNibNamed:owner:options: or calling UIViewController's initWithNibName:bundle:
If you are not instantiating your NumberValidator via it's nib, then the activity property will not be assigned. If you are constructing it via a nib, then you have not assigned the outlet with an appropriate UIActivityIndicatorView in Interface Builder, by CTRL+Dragging your UIActivityIndicatorView to your controller.
I would start by setting a breakpoint in -connectionDidFinishLoading: and verifying that activity is not nil.
This probably works - i.e. the activity indicator starts animating. There may be another problem, though - the GUI is not refreshed until you stop processing the connectionDidFinishLoading method, and therefore it seems that [activity startAnimating] doesn't work. (You can test this by not calling [activity stopAnimating] - it should show up eventually.)
See e.g. this thread (connectionDidFinishLoading - how to force update UIView?) and my response.
Is it (a) not compiling, (b) crashing when it hits there, or (c) just not doing anything? My suspicion is that it's (c), and it's because you don't have an activity indicator there. Try logging the value of activity to the console, and verify its a valid object.
Thanks for the quick responses.
The connectionDidFinishLoading does successfully execute, and I have placed NSLogs to confirm. However, the startAnimating doesn't.
Note:
If I do [activity startAnimating]; within the following then it works...:
QuickStartViewController.m (not NumberValidator.m):
- (IBAction)showPicker:(id)sender {
[activity startAnimating];
...
}

UIView and NSTimer not releasing memory

I have a line of UILabel text in a UIView which is regularly updated via NSTimer. This code is supposed to write a status item near the bottom of the screen every so often. The data comes from outside of its control.
My app runs out of memory really fast because it seems the UILabel is not being released. It seems that dealloc is never called.
Here is a very compressed version of my code (error checking etc removed for clarity.):
File:SbarLeakAppDelegate.h
#import <UIKit/UIKit.h>
#import "Status.h"
#interface SbarLeakAppDelegate : NSObject
{
UIWindow *window;
Model *model;
}
#end
File:SbarLeakAppDelegate.m
#import "SbarLeakAppDelegate.h"
#implementation SbarLeakAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
model=[Model sharedModel];
Status * st=[[Status alloc] initWithFrame:CGRectMake(0.0, 420.0, 320.0, 12.0)];
[window addSubview:st];
[st release];
[window makeKeyAndVisible];
}
- (void)dealloc
{
[window release];
[super dealloc];
}
#end
File:Status.h
#import <UIKit/UIKit.h>
#import "Model.h"
#interface Status : UIView
{
Model *model;
UILabel * title;
}
#end
File:Status.m
This is the where the problem lies. UILabel just does not seem to be released, and quite possible the string as well.
#import "Status.h"
#implementation Status
- (id)initWithFrame:(CGRect)frame
{
self=[super initWithFrame:frame];
model=[Model sharedModel];
[NSTimer scheduledTimerWithTimeInterval:.200 target:self selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
return self;
}
- (void)drawRect:(CGRect)rect
{
title =[[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 12.0f)];
title.text = [NSString stringWithFormat:#"Tick %d", [model n]] ;
[self addSubview:title];
[title release];
}
- (void)dealloc
{
[super dealloc];
}
#end
File: Model.h (this and the next are the data sources, so included only for completeness.) All it does is update a counter every second.
#import <Foundation/Foundation.h>
#interface Model : NSObject
{
int n;
}
#property int n;
+(Model *) sharedModel;
-(void) inc;
#end
File: Model.m
#import "Model.h"
#implementation Model
static Model * sharedModel = nil;
+ (Model *) sharedModel
{
if (sharedModel == nil)
sharedModel = [[self alloc] init];
return sharedModel;
}
#synthesize n;
-(id) init
{
self=[super init];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(inc) userInfo:nil repeats:YES];
return self;
}
-(void) inc
{
n++;
}
#end
The problem is that you are never removing the UILabel from the Status UIView. Let's take a look at your retain counts in drawRect:
(void)drawRect:(CGRect)rect {
title =[[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 12.0f)];
Here, you have created a UILabel with alloc, which creates an object with a retain count of 1.
[self addSubview:title];
[title release];
Adding the UILabel to Status view increases title's retain count to 2. The following release results in a final retain count of 1. Since the object is never removed from its superview, the object is never deallocated.
Essentially, you are adding one UILabel on top of another, each time the timer is fired, until memory runs out.
As suggested below, you should probably create the UILabel once when the view loads, and just update the UILabel's text with [model n].
As a housekeeping note, you might also want to make sure that you are properly deallocating any left over objects in your dealloc methods. 'model' and 'title' should be released in Status' dealloc, just as 'model' should be in SbarLeakAppDelegate.
Hope this helps.
Edit [1]:
It sounds like you have the memory issue pretty well handled at this point. I just wanted to suggest another alternative to the two timers you are using.
The timer you have running in your Status object fires every .2 seconds. The timer which actually increments the 'model' value, n, fires only once each second. While I believe you are doing this to ensure a more regular "refresh rate" of the Status view, you are potentially re-drawing the view 4 or 5 times per second without the data changing. While this is may not be noticeable because the view is fairly simple, you might want to consider something like NSNotification.
With NSNotification, you can have the Status object "observe" a particular kind of notification that will be fired by the Model whenever the value 'n' changes. (in this case, approximately 1 per second).
You can also specify a callback method to handle the notification when it is received. This way, you would only call -setNeedsDisplay when the model data was actually changed.
There are 2 problems with your code.
Problem 1
In -drawRect you add a subview to the view hierarchy every time the view is drawn. This is wrong for 2 reasons:
Every time the view is drawn, the number of subviews increases by 1
You are modifying the view hierarchy at draw time - this is incorrect.
Problem 2
Timers retain their targets. In the initializer for your Status object, you create a timer which targets self. Until the timer is invalidated, there is a retain cycle between the timer and the view, so the view will not be deallocated.
If the approach of using a timer to invalidate the view is really the correct solution to your problem, you need to take explicit steps to break the retain cycle.
One approach to doing this is to schedule the timer in -viewDidMoveToWindow: when the view is being put in a window [1], and invalidate the timer when the view is being removed from a window.
[1] Having the view invalidate itself periodically when it isn't displayed in any window is otherwise pointless.
Instead of calling -setNeedsDisplay with your NSTimer in the view controller, why not create a method that calls "title.text = [NSString stringWithFormat:#"Tick %d", [model n]] ;"? That way instead of re-creating the label every time the timer fires you can just update the value displayed.