I want to display a custom bar that appears on every screen of my application with buttons that work. I add the CustomViewController to my classes in the init method, and everything works correctly, except when I analyze my application I get a potential memory leak.
When I release [customViewController release], the buttons on the CustomViewController will no longer work. What is the proper way to go about implementing this solution with no memory leaks.
#import "CustomViewController.h"
#implementation CustomViewController
- (IBAction)doSomething:(id)sender
{
// Perform an action
}
#end
A ViewController which I create the CustomViewController:
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
CustomViewController *customViewController = [[CustomViewController alloc] initWithNibName:#"CustomViewController" bundle:nil];
UIView *bar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 30)];
[bar addSubview:customViewController.view];
[self.view addSubview:bar];
[bar release];
}
}
You seem to be going about implementing this the wrong way. What you actually need to do is create CustomViewController and add your toolbar to that view. Every other view controller in your app should then be made a subclass of CustomViewController.
If a custom navigation bar is the only thing you're using this superclass for, I would recommend just styling the bar directly on the UINavigationController your app is using.
The proper solution was to create a container view, and place my custom task bar inside of that view.
You said your bar has buttons? Does it release those buttons when it itself is released? Check it's viewDidLoad function.
Related
I am trying to add the Tapku calendar to my app. I am using storyboards, I have added the Tapku library, imported the necessary files and add the TKCalendarMonthViewDelegate methods. I am adding the calendar to a UIView called calendarView. When I run the app the calendar doesn't appear, just the view with nothing inside it.
-(void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:NO animated:YES];
self.navigationItem.hidesBackButton = YES;
calendar = [[TKCalendarMonthView alloc] init];
calendar.delegate = self;
calendar.dataSource = self;
calendar.frame = CGRectMake(0, 0, calendar.frame.size.width, calendar.frame.size.height);
// Ensure this is the last "addSubview" because the calendar must be the top most view layer
[self.view addSubview:calendar];
[calendar reload];
// Do any additional setup after loading the view.
}
Can anyone help me please?
try by specifing frame points directly,like this
calendar.frame = CGRectMake(0, 0, 320,400);
If you're adding TKCalendarMonthView to your view controller using a Storyboard, then you should not also be initializing another instance of TKCalendarMonthView in your view controller's -viewDidLoad method.
In your Storyboard:
Add a TKCalendarMonthView to your view controller.
Set the size contraints.
Connect TKCalendarMonthView to the outlet (see below) in your view controller.
In your view controller:
Add an outlet for the TKCalendarMonthView.
#interface YourViewController () <TKCalendarMonthViewDataSource, TKCalendarMonthViewDelegate>
#property (weak, nonatomic) IBOutlet TKCalendarMonthView *calendarMonthView;
#end
In -viewDidLoad, connect TKCalendarMonthView's delegate and data source. Note, you can also do this in the Storyboard if you first add the IBOutlet annotate to the delegate and dataSource properties in TKCalendarMonthView.h
#implementation YourViewController
...
- (void)viewDidLoad
{
[super viewDidLoad];
...
self.calendarMonthView.delegate = self;
self.calendarMonthView.dataSource = self;
However these changes alone will not get the TKCalendarMonthView to display the calendar. The reason is that the view is getting initialized by the Storyboard but none of the existing -init methods are called when loaded by the Storyboard. So you will need to add an -initWithCoder: method to TKCalendarMonthView.m. The following example will call the default -init: method.
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [self init];
if (self) {
}
return self;
}
If you do all this, you should see the rendered calendar instead of a blank view.
I have 2 view, named viewcontroller1,viewcontroller2. every viewcontroller has load about 3 pictures(load it in XIB) , when i try to use [self.navigationcontroller pushviewcontroller], to push view1 to view2, the memory cannt release.(i want to konw it cannt release viewcontrller1 or cannt release uiimageview)any error for my code ??
viewcontroller1 code just like this:
viewcontroller1.h
#interface ViewController : UIViewController
#property(strong,nonatomic)IBOutlet UIImageView *mainImageView;
#property(strong,nonatomic)IBOutlet UIImageView *topwallImageView;
#property(strong,nonatomic)IBOutlet UIImageView *buttonImageView;
viewcontroller1.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
self.mainImageView = nil;
self.topwallImageView = nil;
self.buttonImageView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
-(void)dealloc
{
[mainImageView release];
[topwallImageView release];
[buttonImageView release];
[super dealloc];
}
-(IBAction)main2ViewController:(id)sender
{
Main2ViewController *main2ViewController =[[Main2ViewController alloc]initWithNibName:#"Main2ViewController" bundle:nil];
[self.navigationController pushViewController:main2ViewController animated:NO];
[main2ViewController release];
}
You have to study more detailed about Object Communication and Memory management in iOS.
Also please try to use ARC. Automatic reference counting (ARC) was introduced in the iOS 5 sdk to free Objective-C programmers from having to handle memory management by making memory management the job of the compiler.
You can convert your project to ARC enabled using this link
You can't release viewcontroller1 and its IBOutlet properties while viewcontroller2 is visible or after pushed viewcontroller2. Because you pushed viewcontroller2 from viewcontroller1.viewcontroller1 is the container/parent of viewcontroller2. viewcontroller2 will not exist with out viewcontroller1
About the IBOutlet imageview images, you can set it to nil .Eg: yourImageview.image = nil; However it will not release the IBOoutlet Imageview memory.
IBOutlet imageview will get released when a release method called to viewcontroller1 because you released it properly in viewcontroller1 dealloc method :)
About your Main2ViewController method:You handled memory properly in your code
When you call the pushViewController , it will retain the controller which is pushed by default. note that your are not the owner. It will release automatically when the controller is popped out.However you have to release the allocated Main2ViewController. So your code is fine :)
I've done this many times with code that is exactly the same, but for some reason it isn't working today.
ExampleViewController1 *exampleView = [[ExampleViewController1 alloc] initWithNibName:#"ExampleViewController1" bundle:nil];
[exampleView setProjectName:[[self.projectListArray objectAtIndex:indexPath.row] objectForKey:#"name"]];
NSLog(#"%#", [[self.projectListArray objectAtIndex:indexPath.row] objectForKey:#"name"]);
XAppDelegate.stackController pushViewController:exampleView fromViewController:nil animated:YES]
My NSLog prints out appropriately.
My ExampleViewController1.h file declared like:
#property(nonatomic, strong) NSString *projectName;
I then do this code in ExampleViewController1.m's
-(void)viewDidLoad {
NSLog(#"%#", self.projectName);
self.projectNameLabel.text = self.projectName;
[super viewDidLoad];
}
The results of my NSLogs are curious. The NSLog from my viewDidLoad appears to be getting called before my other one:
2012-04-22 10:59:41.462 StackedViewKit[43799:f803] (null)
2012-04-22 10:59:41.463 StackedViewKit[43799:f803] NewTest
I have confirmed that the (null) value there is from NSLog(#"%#", self.projectName);, but that should be the second NSLog called...I can't figure out why it is coming through first.
Someone requested this code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// random color
self.view.backgroundColor = [UIColor colorWithRed:((float)rand())/RAND_MAX green:((float)rand())/RAND_MAX blue:((float)rand())/RAND_MAX alpha:1.0];
}
return self;
}
As I expected, the problem is that you are trying to access self.view inside the initialization method. So move the line self.view.backgroundColor = ... to the viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.projectName);
self.projectNameLabel.text = self.projectName;
self.view.backgroundColor = [UIColor colorWithRed:((float)rand())/RAND_MAX green:((float)rand())/RAND_MAX blue:((float)rand())/RAND_MAX alpha:1.0];
}
In fact, the documentation of the view property says:
If you access this property and its value is currently nil, the view controller automatically calls the loadView method and returns the resulting view.
So when you call self.view in the initialization method, the view controller will have to load the view (from the nib or using the loadView method). And that's why viewDidLoad is called.
viewDidLoad is called before a view controller is displayed for the
first time, not immediately after initWithNibName.
> viewDidLoad method is called after the view controller has loaded its view
hierarchy into memory. This method is called regardless of whether the
view hierarchy was loaded from a nib file or created programmatically
in the loadView method.
> initWithNibName The nib file you specify is not loaded right away. It
is loaded the first time the view controller’s view is accessed. If
you want to perform additional initialization after the nib file is
loaded, override the viewDidLoad method and perform your tasks there.
You can use App delegate to pass the data from one to another, that is another alternate solution.
you do in initWithNibName method itself. or in viewDidAppear.
Your initWithNibName method should be like this as per as #sch comments;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil] //just set it here first and then check
if (self) {
// do something here;
}
return self;
}
We just need to be smart enough to think about what do we need to in constructor and what do we need to at viewDidLoad (once it had loaded into memory)
my app starts with a tab bar controller which have 5 tabs. At start the first one in presented with its name but the other four don't have a name until I click on them. Then the name appears depending which language the user has.
How can I set the name of the tabs before the tab bar appears?
I am using storyboard. Is there a way to set title at the tab bar programmatically when all the rest is done with storyboard? I tried in the AppDelegate something like [FirstViewController setTitle:NSLocalizedString(#"Titel1", nil)];
But I got an error that there is no class method for selector setTitle.
Thanks.
I had the same problem today. I'm using storyboard too. In my case i used the following way.
I've created a new "Objective-C class" with the name
"MainTabBarViewController" as a subclass of "UITabBarController".
In my storyboard in "identity inspector" i changed "Custom class" to "MainTabBarViewController".
In the method "viewDidLoad" in "MainTabBarViewController" i added:
[[self.viewControllers objectAtIndex:0] setTitle:NSLocalizedString(#"home", nil)];
[[self.viewControllers objectAtIndex:1] setTitle:NSLocalizedString(#"statistics", nil)];
[[self.viewControllers objectAtIndex:2] setTitle:NSLocalizedString(#"settings", nil)];
[[self.viewControllers objectAtIndex:3] setTitle:NSLocalizedString(#"info", nil)];
I guess it is not the perferct way, but for me it works perfect.
I had a configuration with two tabs like this:
MainTabBarController : UITabBarController
+- MessagesNavigationController : UINavigationController
+- ContactsNavigationController : UINavigationController
In a configuration with Storyboard and ARC I overrode the initWithCoder: selector in the custom classes of my UITabBarController view controllers (in this case ContactsNavigationController) and initialized the tabBarItem.title there like this:
#implementation ContactsNavigationController
...
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self) {
self.tabBarItem.title = NSLocalizedString(#"Contacts", nil);
}
return self;
}
When using storyboards the iOS calls (id)initWithCoder: instead of -(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil when the storyboard view controller is initialized during UITabBarController launch. Also note that viewDidLoad doesn't get called until the view controller tab is selected from the tab bar.
In the app delegate, where you are creating the view controllers, set the title property here (rather than in viewDidLoad), for example:
vc1 = [[VC1 alloc] init];
vc1.title = #"List";
vc2 = [[VC2 alloc] init];
vc2.title = #"Map";
tabBarController.viewControllers = [[NSArray alloc] initWithObjects:vc1, vc2, nil];
If you take a look at the FirstViewController created by Xcode using the template Tab-Bar application based in the - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil method you can see something like this:
if (self) {
//check your language
self.title = NSLocalizedString(#"First", #"First");
self.tabBarItem.image = [UIImage imageNamed:#"first"];
}
So you have to check for your language and then set the title property, this will set the title on the tab bar and in the navigation bar.
Try this, in every view u have to use this method
Suppose this is you first view
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"homepage", #"homepage");
[self.tabBarItem setTag:0];
self.tabBarItem.image = [UIImage imageNamed:#"homepage"];
}
return self;
}
In you app delegate class write this code to add UITabBarController
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2,viewController3,viewController4,viewController5, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
Add above code in
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {}
I have created a navigation-based application. In that, I have created MyTableViewController using uiviewcontroller.class.
#import "RootViewController.h"
#import "MyTableViewController.h"
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MyTableViewController *tableViewController = [[MyTableViewController alloc] init];
}
#end
#import "MyTableViewController.h"
#implementation MyTableViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"sedfsdsd");
}
#end
I don't want to show the view when the instance is created. I want to call the constructor method. I don't know how to do it. Please help me out.
#Caroline have described very good.
A normal method of your class could serve your purpose and you can name that something ViewContruction and define it in your MyTableViewController class.
-(void) ViewContruction
{
//Create all your views here
//Add that to the self.view of your controller
}
Call the above function explicitly on the instance of your view controller.
Just creating a UIViewController instance does not load the view.
If you have something like [self.view addSubview:tableViewController.view] then when that statement is executed, viewDidLoad will get executed.
However, if it's a navigation-based app, then you will need to push the viewcontroller to see it, rather than adding the subview as above.
For example:
Settings *settingsController = [[Settings alloc] initWithNibName:#"Settings" bundle:nil];
settingsController.contentSizeForViewInPopover = settingsController.view.frame.size;
[self.navigationController pushViewController:settingsController animated:YES];
[settingsController release];