How to perform a [self.view addSubview: lbl] outside of ViewController Class scope? - objective-c

How to perform a [self.view addSubview: lbl] outside of ViewController Class scope ?
or:
How do I add a label or another view in the mainview, outside of the ViewController class, in a different class?
thanks
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[lbl setText:#"hi there"];
[self.view addSubview:lbl];// <-- this works, but ...
// what is "self" referring to?
// and how can I declare and call from another class?
...
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[lbl setText:#"hi there"];
calcRomanAppDelegate *v = [[calcRomanAppDelegate new] init];
[v.viewController.view addSubview:lbl]; // this compiles, but...
// fails to shows a label on the form
...
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[lbl setText:#"hi there"];
calcRomanViewController *v = [[calcRomanViewController new] init];
[v.view addSubview:lbl]; // this just makes a call back to viewDidLoad... endless loop
}

Well, view is just a property of the UIViewController class. Assuming that you have your UIViewController *controller variable somewhere, you can just use
[controller.view addSubview:subview]

The reason that [v.viewController.view addSubview:lbl]; doesn't work is that v is a new instance of calcRomanAppDelegate. Every application has a shared instance of the app delegate, that can be accessed via [[NSApplication sharedApplication] delegate]. Therefore, your code would become:
calcRomanAppDelegate *v = (calcRomanAppDelegate *)[[NSApplication sharedApplication] delegate];
[v.viewController.view addSubview:lbl]; // this compiles but shows a blank form
Also In the code that you wrote, I will point out that the new method returns an initialized object, so you do not need the extra call to init in [[calcRomanAppDelegate new] init]. Instead of using the new method, I suggest using alloc, which doesn't call the initializer. Obviously that is not the issue in this particular case, but it's an important thing to know.

Not sure what you tried to accomplish.
But let's say you are in the view1 and want to create another view (view2) with your UILabel lbl added to this view2. Here are what you would do:
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, h)]; //x,y, w h are for your view2
[view addSubview:lbl];
[self.view addSubview:view2]; //self is your current viewcontroller - you add view2 on top of view1
On the other hand if you already have a ViewController class ViewController2.h, ViewController2.m and a ViewController2.xib defined. Here are what you would do:
ViewController2 *viewController2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[viewController2.view addSubview:lbl];
[self.view addSubview:viewController2.view]; //same as before, you need to add viewController2's view to the current view
Hope this help.

Related

Why is *drawRect* not being called from the ViewController?

I have an iPad app (XCode 5, iOS 7, Storyboards and ARC). I have a UIPopover created in a UIViewController, and when I tap a button it correctly displays the popover.
Now, I want to draw a grid in that popover; I know I have to add code to drawRect do do the drawing, and do a setNeedsDisplay to get the drawing done. However, it's not working!
My question is why is drawRect not being called when from [popoverView setNeedsDisplay]; ? Both methods are in the same UIViewController class.
Here is the code that is supposed to call the drawRect
// create popover
UIViewController* popoverContent = [[UIViewController alloc] init];
UIView *popoverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 650, 416)];
popoverView.backgroundColor = [UIColor whiteColor];
popoverContent.preferredContentSize = CGSizeMake(650.0, 416.0);
// draw the lines, etc on the popoverContent
[popoverView setNeedsDisplay];
// create the popover controller and attach the popover content to it
popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverContent];
popoverController.delegate = (id)self;
[popoverController setPopoverContentSize:CGSizeMake(650, 416) animated:NO];
[popoverController presentPopoverFromRect:CGRectMake(650, 416, 10, 50) inView: obViewOpenAppts
permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
I have a breakpoint in drawRect so I know it's not being called.
What am I doing wrong?
drawRect: is a UIView method, not a UIViewController method. If you want to override it you have to subclass UIView, and then create it like this:
MyUIViewSubclass *popoverView = [[MyUIViewSubclass alloc] initWithFrame:CGRectMake(0, 0, 650, 416)];

UIPopoverController trouble

I'm trying to create a popover with the following code:
//.h
#interface LoginViewController : UIViewController <UIPopoverControllerDelegate>
....
#end
//.m
- (IBAction)popoverTest:(id)sender
{
UIViewController *popoverContent = [[UIViewController alloc] init];
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
UILabel *nameLabel = [[UILabel alloc] init]; //edited, fixed UILabel allocation
nameLabel.text = #"Person's name";
[nameLabel sizeToFit];
nameLabel.frame = CGRectMake(10, 10, nameLabel.frame.size.width, nameLabel.frame.size.height);
[myView addSubview:nameLabel];
popoverContent.view = myView;
popoverContent.contentSizeForViewInPopover = CGSizeMake(300, 300);
UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverContent];
popoverController.delegate = self;
[popoverController presentPopoverFromRect:((UIButton *)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
NSLog(#"ran all code");
}
I created a UIView, put a label as a subview and then assigned my view to the UIViewController.view. Then I created a popover controller, sized the popover controller, set the delegate and presented it from the button's frame.
I receive a SIGABRT and the app crashes.
Is there something I'm missing?
EDIT: I fixed the UILabel allocation. The problem is always there.
Your code is quite strange.
For example why do you create a view and do you add it to the content view of your popover?
Then, you have to make attention to memory leaks. There a lot of them in your code.
That said, here a simple example for display a UIViewcontroller within a UIPopoverController.
- (IBAction)yourAction:(id)sender
UIButton* senderButton = (UIButton*)sender;
UIViewController* yourController = [[UIViewController alloc] init];
UIPopoverController* pop = [[UIPopoverController alloc] initWithContentViewController:yourController];
pop.delegate = self;
pop.popoverContentSize = CGSizeMake(300, 300);
self.popover = pop;
[pop presentPopoverFromRect:senderButton.bounds inView:senderButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
pop.passthroughViews = nil;
[yourController release];
[pop release];
}
where self.popover is a #property with a retain policy. In this manner in UIPopoverControllerDelegate methods (or wherever you want), you can release your popover and/or dismiss it.
Hope it helps.
P.S. Check the code because I've written by hand.
Edit
Usually when you create a popover, its content view controller is or a custom UIViewController or a UINavigationController (in this case you want to take advantage of its navigation bar).
For example, instead of the simple UIViewController, you could create a custom one
//.h
#interface CustomUIViewController : UIViewController
#end
//.m
#implementation CustomUIViewController
// other code here...
- (void)viewDidLoad
{
// here you are sure that the view has been loaded in memory (alternatively override loadView method), so
UIView* greenView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
green.backgroundColor = [UIColor greenColor];
[self.view addSubview:greenView];
[greenView release];
}
#end
and use it within a a popover
- (IBAction)yourAction:(id)sender
UIButton* senderButton = (UIButton*)sender;
CustomUIViewController* yourController = [[CustomUIViewController alloc] init];
// same as before...
}
solved my issue from this question.
short answer: ARC doesn't retain the UIPopoverController.
i made it an instance variable and works just fine.
The problem seems to be the following:
UILabel *nameLabel;
nameLabel.text = #"Person's name";
nameLabel is uninitialized. It points to random memory, and when calling -setText: through the text-property, your application crashes.
Apart from this, you have a few memory leaks. Remember: If you call alloc, you have to release it somewhere.
(I'm not sure how this works with the new automatic alloc/release).

Creating a common UIToolbar helper file and methods for Objective C

I have build a view with a UIToolbar which is working great.
This toolbar will be appearing right across the app, and right now I am copy/pasting the code into lots of different files.
I do not want to repeat myself, and am looking to create a helper file that will include the toolbar setup and the methods linked to the toolbar in every file I need.
I've tried putting the following code into a .h .m file and inheriting from UIView, but there is a problem because there is a reference to self.navigiationItem
Is there a way that I can create a common Objective C file that will have all the code and methods I want to use?
Thanks.
- (void)viewDidLoad
// ...
// appears in viewDidLoad
// ---- TOOLBAR -----------//
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 100.0, 44.01f)];
//[toolbar setBackgroundColor:[UIColor blackColor]];
//[toolbar setTintColor:[UIColor redColor]];
//[toolbar.layer setBorderColor:[[UIColor redColor] CGColor]];
// Bar buttons
UIBarButtonItem *barReloadBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(btnReload:)];
[barReloadBtn setStyle:UIBarButtonItemStyleBordered];
// Profile bar button
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"111-user" ofType:#"png"]];
UIBarButtonItem *barProfileBtn = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStyleBordered target:self action:#selector(btnProfile:)];
// Button array
NSMutableArray *buttons = [[NSMutableArray alloc] init];
[buttons addObject:barProfileBtn];
[buttons addObject:barReloadBtn];
[toolbar setItems:buttons];
// Set nav items
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
// memory cleanup
[image release];
[buttons release];
[barReloadBtn release];
[barProfileBtn release];
[toolbar release];
// ---- /TOOLBAR -----------//
}
#pragma mark - IBActions
-(IBAction) btnProfile:(id)sender
{
UserProfileVC *userProfileVC = [[UserProfileVC alloc] initWithNibName:#"UserProfileVC" bundle:[NSBundle mainBundle]];
UINavigationController *tmpNavCon = [[UINavigationController alloc] initWithRootViewController:userProfileVC];
[self.navigationController presentModalViewController:tmpNavCon animated:YES];
[tmpNavCon release];
[userProfileVC release];
}
-(IBAction) btnReload:(id)sender
{
NSLog(#"Not done yet");
}
navigationItem is a property of UIViewController, not UIView. If you've got common functionality like this, I would inherit from UIViewController, add your custom logic to viewDidLoad (or wherever is appropriate) and then inherit your view controllers from that class. Just make sure you call [super viewDidLoad] from your subclasses' implementations of viewDidLoad.

Navigation Control Problem

I am using some methods to load a new .xib and go back to the main menu. However after about five time it crashes by using too much memory. I need to be able to go back to the main menu and to the game many times. Any other methods I should use for the navigation controls.
Main Menu part:
GameViewController* game = [[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:game animated:NO];
Game part to return to main menu:
[self.navigationController popViewControllerAnimated:NO];
Here is the viewdidLoad
{
- (void)viewDidLoad {
[super viewDidLoad];
[self StartTimer];
TotalSeconds = 0;
GameCenterTotalSeconds = 0;
timeSec = 0;
timeMin = 0;
Background = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 480, 320)] autorelease];
Background.image = [UIImage imageWithContentsOfFile:[ [ NSBundle mainBundle] pathForResource:#"Background" ofType:#"png"]];
[self.view addSubview:Background];
timeLabel.textColor = [UIColor redColor];
[self.view addSubview:timeLabel];
NumberLabel = [[[UIImageView alloc] initWithFrame:CGRectMake(0, -4, 60, 70)] autorelease];
NumberLabel.image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"Number" ofType:#"png"]];
[self.view addSubview:NumberLabel];
QuestionNumber = [[[UILabel alloc] initWithFrame:CGRectMake(23, 17, 20, 20)] autorelease];
QuestionNumber.text = #"1";
QuestionNumber.textColor = [UIColor redColor];
QuestionNumber.backgroundColor = [UIColor clearColor];
[QuestionNumber setFont:[UIFont fontWithName:#"Marker Felt" size:30]];
[self.view addSubview:QuestionNumber];
numberLives = 1;
appDelegate = (OppositeMoronTestAppDelegate *)[[UIApplication sharedApplication]delegate];
musicButton = [[[UIButton buttonWithType:UIButtonTypeCustom] retain] autorelease];
musicButton.frame = CGRectMake(5, 283, 35, 35);
musicButton.backgroundColor = [UIColor clearColor];
if (appDelegate.shouldPlayMusic == YES) {
UIImage *Image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"MusicOn" ofType:#"png"]];
[musicButton setBackgroundImage:Image forState:UIControlStateNormal];
[musicButton addTarget:self action:#selector(TurnMusicOff) forControlEvents:UIControlEventTouchUpInside];
} else {
UIImage *Image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"MusicOff" ofType:#"png"]];
[musicButton setBackgroundImage:Image forState:UIControlStateNormal];
[musicButton addTarget:self action:#selector(TurnMusicOn) forControlEvents:UIControlEventTouchUpInside];
}
[self.view addSubview:musicButton];
[self showQuestion1];
}
}
try autorelease on your view controller:
GameViewController* game = [[[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil] autorelease];
The navigation controller will take ownership of the view controller passed to it, so you don't have to keep a reference to it. But you can't keep allocating GameViewControllers over and over without releasing them. autorelease is useful for that. You could also release it after you've passed it to the navigation controller if you prefer:
GameViewController* game = [[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:game animated:NO];
[game release];
game = nil;
EDIT:
So if you're already releasing the game object, then it must be a memory leak within the GameViewController class itself.
Annything you alloc, copy or retain in your GameViewController class you're supposed to release in the dealloc method (and maybe also in the viewDidUnload method if you're alloc/copy/retaining in the viewDidLoad method).
The iOS Memory Management Programming Guide might be helpful if you want to get into more detail.
If you want to post the relevant code from the GameViewController class, I'm sure someone will be able to help you pin down the memory leak.
You can also try the Leaks tool in Instruments
EDIT 2:
I'm assuming you have several IBOutlets connected to properties in your GameViewController class...
don't know if you're already doing this, but in your viewDidUnload method AND on your dealloc method you have to set all of these IBOutlets properties to nil in order to release them, like so:
- viewDidUnload
{
//... whatever comes before
self.timeLabel = nil;
self.NumberLabel = nil;
//... etc
}
- dealloc
{
//... whatever comes before
self.timeLabel = nil;
self.NumberLabel = nil;
//... etc
[super dealloc];
}
In general, if you have any properties declared with retain, that means that when you set that property the object will be retained. If you set that property to nil, the object that was there will be released for you. So any properties with the retain keyword should be set to nil (or the backing ivar released).

Where's the memory leak?

I'm adding a custom view to a tableHeaderView with the following code:
imageButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
imageButton.frame = CGRectMake(120, 12, 64, 64);
imageButton.titleLabel.font = [UIFont systemFontOfSize:10];
imageButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
imageButton.titleLabel.textAlignment = UITextAlignmentCenter;
[imageButton setTitle:NSLocalizedString(#"Choose\nPhoto", #"Choose\nPhoto") forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(photoButtonPressed) forControlEvents:UIControlEventTouchUpInside];
// add existing image, if any, to button
if (child.thumbnailImage != nil) {
[imageButton setBackgroundImage:child.thumbnailImage forState:UIControlStateNormal];
}
// add button to view
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(22, 12, 70, 70)];
[headerView addSubview:imageButton];
// add view to table header
self.tableView.tableHeaderView = headerView;
The memory leak is showing on the alloc line above for the headerView UIView.
I'm declaring the UIView and the UIButton in the header file and releasing it in ViewDidUnload and dealloc, so I'm not sure what I'm doing wrong.
Also, this is only showing up on the device and not the simulator (just thought I'd mention).
Any help would be appreciated.
Thanks,
Rod
You're using the headerView setter method (self.headerView) so you need to release the UIView instance you assign to the headerView property, either using release or autorelease.
e.g.
UIView* newHeaderView = [[UIView alloc] initWithFrame...];
self.headerView = newHeaderView;
[newHeaderView release];
or
self.headerView = [[[UIView alloc] initWithFrame:...] autorelease];
The reason is because the headerView setter method automatically retains the object assigned to it.
Alternatively, you can set the headerView instance variable directly, without using the property setter method:
[headerView release]; // release any existing object
headerView = [[UIView alloc] initWithFrame:...];