My application has a view controller that extends UITableViewController. The initialization method looks like this:
- (id)initWithCoder:(NSCoder*)coder {
if (self = [super initWithCoder:coder]) {
self.tableView = [[UITableView alloc] initWithFrame:self.tableView.frame
style:UITableViewStyleGrouped];
}
return self;
}
When the view is initially loaded, it's displayed as UITableViewStyleGrouped. However, if my app ever receives a low memory warning, the above view changes to UITableViewStylePlain. There is no associated xib file with the View/Controller. The viewDidUnload and didReceiveMemoryWarning methods are straightforward:
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
My question is, why does the table style change when I receive a memory warning?
It happens on the device and on the simulator. The device is a 3G iphone running OS 3.1.3, the simulator is running OS 3.1
In your initialization, you call [super initWithCoder:coder]. It would probably be better to override the designated initializer for UITableViewController, which is -initWithStyle:. What's probably happening is that when you create the table view controller by calling [super init…], it's being created with its tableView property already being set; that is, it's creating the table view on initialization. That's why your call to self.tableView.frame works—that shouldn't work if the value of self.tableView is nil. Here's a better implementation:
- (id)initWithCoder:(NSCoder*)coder {
if (self = [super initWithStyle:UITableViewStyleGrouped]) {
}
return self;
}
Related
I have a view controlled app. On my xib I have no webviews but I do have buttons that bring up classes that have webviews. So hit button one and a uiwebview pops up so on and so forth.
Now in one of my classes I pull up a remote webpage that has a link on it. I want to over-ride that button link with shouldStartLoadwithrequest. How ever this never seems to get called.
Right now my class is called Tenth.m and I have this code in my view controller
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#" Test spot 1");
NSString* scheme = [[request URL] scheme];
if ([#"didTap" isEqual:scheme]) {
// Call your method
NSLog(#" Test spot 2");
[self didTapButton1];
return NO;
} else {
NSLog(#" Test spot 3");
return YES;
}}
But I get nothing (none of my NSLogs). I have tried putting this into my Tenth.m class and still nothing. I have looked at other examples and even downloaded one but it only uses the viewcontroller and delegates classes. No third class.
I'm lost as to why its not getting called.
Here is my third class
#import "Tenth.h"
#implementation Tenth
- (void)awakeFromNib
{
[category10 loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.mywebsite.com/api/didtap.php"]]];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#end
remote page code
hello
Tenth.h file
#import <UIKit/UIKit.h>
#interface Tenth : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *category10;
}
-(IBAction)back;
#end
My first guess is that the Web view does not have its delegate set. In order for it to call
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
The class containing this method needs to be set as the webview's delegate. So somewhere in the code there should be a line similar to webView.delegate = <variable>, where the variable is the instance of the class which has implemented the shouldStartLoadWithRequest: method.
This is often "self" when the webView is a subview of a viewController's view. But the way you have described your application, I am not sure if it will be self or not, but it sounds like it will not be self, but the name of the instance of a different class.
Maybe try this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Remember to set web view delegate to self.
category10.delegate = self;
}
Ok, I'm having a lot of problems right now trying to get initWithCoder: to work right. I have a nib file that gets loaded, and in my app delegate, I call unarchiveWithFile: for the view controller that is associated with that nib, and now my app crashes. I can see that initWithCoder: is being called twice, presumably once from when awakeFromNib: is called, and then from when I call unarchiveWithFile: since the view controller conforms to NSCoding. But now either it crashes as soon as the view loads or when I press an IBOutlet. Any suggestions??
Edit: Here's the code for initWithCoder:
- (id)initWithCoder:(NSCoder *)coder {
[super initWithCoder:coder];
[[self mapView] addAnnotations:[[coder decodeObjectForKey:#"Annotations"] retain]];
return self;
}
All I'm doing is decoding an array of annotations for a map view, but somewhere along the line this method is being called twice and then it crashes.
Don't forget to put the nil check in your init methods. E.g. the method you posted would be more correct if you wrote it as:
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[[self mapView] addAnnotations:[[coder decodeObjectForKey:#"Annotations"] retain]];
}
return self;
}
That's not the cause of your problem, however.
Is there are good reason for you unarchiving your view controller yourself? If you're not doing anything special, you can rely on the existing mechanisms to do it. The default implementation of init for a UIViewController looks for a nib with the same name as your view controller, and if it exists, it loads the nib (via initWithNibName).
If there is data which you need to archive in and out, it may be that it shouldn't be actually part of the UIViewController. Factor it out elsewhere perhaps?
you can try
- (id)initWithCoder:(NSCoder *)coder {
if(self == nil)
{
[super initWithCoder:coder];
[[self mapView] addAnnotations:[[coder decodeObjectForKey:#"Annotations"] retain]];
}
return self;
}
Using the PhotoScroller example from Apple, to reuse the memory allocated for views, I cannot get the memory released once the retain count hits 0. Here my code for a better understanding:
This piece is an extract from PhotoScroller
PhotoViewController.m
- (void)configurePage:(ImageScrollView *)page forIndex:(NSUInteger)index
{
page.index = index;
page.frame = [self frameForPageAtIndex:index];
[page displayPage:index];
}
ImageScrollView.h
#interface ImageScrollView : UIView
{
UIViewController *vc;
NSUInteger index;
}
#property (assign) NSUInteger index;
- (void)displayPage:(int)indice;
#end
ImageScrollView.m
- (void)dealloc
{
[vc release];
[super dealloc];
}
- (void)displayPage:(int)indice
{
//remove previous view
[vc.view removeFromSuperview];
[vc release];
vc = nil;
//NSLog(#"vc retain %i", [vc retainCount]);
// make a new viewController for the new page
Class clss = NSClassFromString([NSString stringWithFormat:#"page_0%i", indice + 1]);
vc = [[clss alloc] initWithNibName:[NSString stringWithFormat:#"page_0%i", indice + 1] bundle:nil];
[self addSubview:vc.view];
}
The classes "page_0x" are UIViewControllers. So far have nothing but a UIImageView on the XIB.
An example of page_01:
#implementation page_01
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Memory peaks to 148 MB on device. Gives me a memory warning Level 1, then releases some memory. Goes down to 95 MB. Keeps going up and down.
Perhaps your page_0x view controllers are getting freed, but something they've created isn't. Some things to check:
Set a breakpoint in the dealloc method of your page_0x view controllers. Is it getting called?
Check all the IBOutlets and other instance variables of your page_0x view controllers. Are they all being released properly in their class's dealloc method?
Run Build & Analyze. Does it turn up anything?
Try running the Leaks Instrument. It can tell you what kind of objects are actually leaking.
(EDIT) Grasping at straws now. You don't have NSZombieEnabled turned on, do you?
(EDIT 2) You say you're throwing a .png on it. What happens if you remove that .png?
If you're running in the simulator, try it on the device. (See this question.)
I have a UIViewController and I would like it to call drawRect so i can draw on the view but nothing happens.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
...code...
}
#end
and the implementation
#import "ViewController.h"
#implementation ViewController
-(void) drawRect:(CGRect) rect {
draw a pony
}
- (void)viewDidLoad {
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
But no pony gets drawn on the view when the app is run, what do i do wrong?
The UIViewController don't have overridden message drawRect.
You should create custom class derived from UIView and override message drawRect there.
You can override message (UIView *)view for UIViewController and return own custom UIView or in Interface Builder change class from UIView to own class.
Calling setNeedsDisplay on your UIView, not the ViewController, is one way of triggering and re-triggering your drawRect code, and you can send this message conditionally whenever you need to update the entire view.
You can call it within a custom UIView using
[self.setNeedsDisplay]
or from the View Controller by making the UIView a property of the controller and calling
[self.myView setNeedsDisplay]
I'm trying to create an application that uses the iPhone MapView (under Google Code). I can't figure out how to integrate this thing into my application without handwriting the entire UI, ie not using the IB at all.
How can I use the Interface builder to create all of my menus and such but add the MapView in? Do I need to edit the MapView to make it an IB component?
Thanks!
EDIT:
#pgb
Here is my code, it still just displays a blank UIView, I have connected everything up on the IB side.
//
// NewTestViewController.h
// NewTest
//
#import <UIKit/UIKit.h>
#interface NewTestViewController : UIViewController {
UIView* mapPlaceholder;
}
#property (nonatomic, retain) IBOutlet UIView* mapPlaceholder;
#end
//
// NewTestViewController.m
// NewTest
//
#import "NewTestViewController.h"
#import "MapView.h"
#implementation NewTestViewController
#synthesize mapPlaceholder;
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
MapView *mapView = [[[MapView alloc] initWithFrame:
[[UIScreen mainScreen] applicationFrame]] autorelease];
[mapPlaceholder addSubview:mapView];
}
return self;
}
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[mapPlaceholder.subviews release];
[super dealloc];
}
#end
NEVERMIND FIGURED IT OUT THANGS PBG
You can probably create the whole interface in IB, add an empty UIView as the placeholder view, and then use addSubview: to add the MapView instance to the view hierarchy.
Your placeholder view can be defined as an IBOutlet, so you can then add the MapView from your UIViewController instance.
In IB you can add a UIView and then change the type of the view to any custom UIView - I'm assuming MapView subclasses UIView just as MKMapView does...
I'm trying to create an application that uses the iPhone MapView (under Google Code).
Why not use MapKit? Surely IB comes with an MKMapView in its Library.