Sounds like a question that has been answered... maybe but i have checked all of the possible solutions i believe and nothing will work for me.
My .h file looks like this
#import <UIKit/UIKit.h>
#interface epsMenuPage : UIViewController<UITableViewDelegate,UITableViewDataSource>{
NSArray *listData;
...
UITableView *menuList;
...
}
#property (nonatomic,retain) NSArray *listData;
...
#property (nonatomic, retain) IBOutlet UITableView *menuList;
...
#end
My .m file looks like this
#implementation epsMenuPage
#synthesize listData;
...
#synthesize menuList;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (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
- (void)viewDidLoad
{
//Arrays credit,debit and both
creditListData = [[NSArray alloc]initWithObjects:#"Return",#"Force Sale",#"Authorize Only",#"Total Sales",#"Items Sold",#"Reciept's", nil];
debitListData = [[NSArray alloc]initWithObjects:#"Return",#"Force Sale",#"Authorize Only",#"Total Sales",#"Items Sold",#"Reciept's", nil];
bothListData = [[NSArray alloc]initWithObjects:#"Batch History",#"Settle Batch", nil];
self.listData = creditListData;
[menuList reloadData];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.view.backgroundColor =[UIColor colorWithRed:255/255.0 green:95/255.0 blue:95/255.0 alpha:1.0];
[colorChange setBackgroundColor:[UIColor colorWithRed:255/255.0 green:95/255.0 blue:95/255.0 alpha:1.0]];
[self.view addSubview:creditTabButton];
[self.view addSubview:debitTabButton];
[self.view addSubview:bothTabButton];
[self.view addSubview:creditButtonText];
[self.view addSubview:debitButtonText];
[self.view addSubview:bothBottonText];
[self.view addSubview:colorChange];
}
- (void)viewDidUnload
{
[self setTitleBAckButton:nil];
[self setTitleSettingsButton:nil];
[self setCreditTabButton:nil];
[self setDebitTabButton:nil];
[self setBothTabButton:nil];
[self setMenuList:nil];
[self setColorChange:nil];
[self setCreditButtonText:nil];
[self setDebitButtonText:nil];
[self setBothBottonText:nil];
[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 (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.listData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SimpleTableIdentifier = #"tableID";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [self.listData objectAtIndex:row];
return cell;
}
- (void)dealloc {
[titleBAckButton release];
[titleSettingsButton release];
[creditTabButton release];
[debitTabButton release];
[bothTabButton release];
[menuList release];
[colorChange release];
[creditButtonText release];
[debitButtonText release];
[bothBottonText release];
[listData release];
[super dealloc];
}
- (IBAction)titleBackClick:(id)sender {
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
- (IBAction)titleSettingsClick:(id)sender {
}
- (IBAction)creditTabClick:(id)sender {
self.listData = creditListData;
[menuList reloadData];
[colorChange setBackgroundColor:[UIColor colorWithRed:255/255.0 green:95/255.0 blue:95/255.0 alpha:1.0]];
[self.view addSubview:creditTabButton];
[self.view addSubview:creditButtonText];
[self.view addSubview:colorChange];
self.view.backgroundColor = [UIColor colorWithRed:255/255.0 green:95/255.0 blue:95/255.0 alpha:1.0];
}
- (IBAction)debitTabClick:(id)sender {
self.listData = debitListData;
[menuList reloadData];
[colorChange setBackgroundColor:[UIColor colorWithRed:244/255.0 green:133/255.0 blue:33/255.0 alpha:1.0]];
[self.view addSubview:debitTabButton];
[self.view addSubview:debitButtonText];
[self.view addSubview:colorChange];
self.view.backgroundColor = [UIColor colorWithRed:244/255.0 green:133/255.0 blue:33/255.0 alpha:1.0];
}
- (IBAction)bothTabClick:(id)sender {
self.listData = bothListData;
[menuList reloadData];
[colorChange setBackgroundColor:[UIColor colorWithRed:72/255.0 green:72/255.0 blue:255/255.0 alpha:1.0]];
[self.view addSubview:bothTabButton];
[self.view addSubview:bothBottonText];
[self.view addSubview:colorChange];
self.view.backgroundColor = [UIColor colorWithRed:72/255.0 green:72/255.0 blue:255/255.0 alpha:1.0];
}
#end
I really for the life of me can not figure out why it wont display to my TableView, i have tried so many different tutorials.
any help is greatly appreciated.
You're never adding the menuList table view to your view. Add it and see if that does the trick:
// In -viewDidLoad
[self.view addSubview:menuList];
EDIT 1: Did you declare properties in your .h? These aren't visible, so it appears as if you're not creating or adding your table view to the view controller's view. However, if you are using IBOutlets, then this probably isn't the issue.
EDIT 2: From the comments below, your dataSource is nil, which is exactly why nothing is being displayed. Make sure your table view in Interface Builder has its dataSource outlet pointing to your view controller. To be double sure, delete the table view from Interface Builder and add it back again. Make sure your connections are exactly right. Also try cleaning the project and rebuilding, as well as restarting Xcode.
It looks like you are missing the required datasource method:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; {
return 1;
}
Also, it looks like you are not actually initializing your UIButton's, or anything you are adding as a subview. Are you using a nib?
Related
I have inherited some old iOS code and have attempted to integrate it into a new iOS 6 application. I have implemented most of the code and so far everything has worked. I'm now working on the last bit of that old code. I'm implementing a set of views to show a rss for a news section of my app. I've implemented the categories view, which upon selecting an item would display the individual items within that category. However nothing gets displayed. I've made all the modifications that I'm aware of that I needed to do, however I'm no expert at iOS development and am in need of some guidance. Below is a snapshot of the simulator as it's attempting to display the view, and below that is a copy of my .h and .m files. I don't know what is preventing anything in the table from showing up. And preemptive thanks to any help!
here's the snapshot of the simulator
Here is a snapshot of the storyboard showing the linking to the Table View
Here's the .h file
#import <UIKit/UIKit.h>
#import "BlogRssParser.h"
#class BlogRssParser;
#class BlogRssParserDelegate;
#class BlogRss;
#class XMLCategory;
#interface NewsViewController : UIViewController <UITableViewDataSource,UITableViewDelegate, BlogRssParserDelegate> {
BlogRssParser * _rssParser;
XMLCategory * _currItem;
}
#property (nonatomic, retain) BlogRssParser * rssParser;
#property (readwrite, retain) XMLCategory * currItem;
#property (nonatomic, retain) IBOutlet UITableView *itemTableView;
#end
Here is my .m file
#import "NewsViewController.h"
#import "NewsDetailsViewController.h"
#import "BlogRssParser.h"
#import "BlogRss.h"
#import "XMLCategory.h"
#define kLabelTag 1;
#interface NewsViewController ()
#end
#implementation NewsViewController
#synthesize rssParser = _rssParser;
#synthesize currItem = _currItem;
- (void)navBarInit {
UIBarButtonItem *refreshBarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
target:self action:#selector(reloadRss)];
[self.navigationItem setRightBarButtonItem:refreshBarButton animated:YES];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.itemTableView.delegate = self;
self.itemTableView.dataSource = self;
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self navBarInit];
[self.itemTableView reloadData];
self.itemTableView.userInteractionEnabled = NO;
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
_rssParser = [[BlogRssParser alloc]init];
_rssParser.delegate = self;
[[self rssParser]startProcess:[_currItem categoryId]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)reloadRss{
[[self rssParser]startProcess:[_currItem categoryId]];
[[self itemTableView]reloadData];
}
- (void)processCompleted{
[[self itemTableView]reloadData];
// _tableView.userInteractionEnabled = YES;
[[self itemTableView]setUserInteractionEnabled:YES];
}
-(void)processHasErrors{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"My Title" message:#"Unable to retrieve the news. Please check if you are connected to the internet."
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [[[self rssParser]rssItems]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
const CGFloat LABEL_TITLE_HEIGHT = 70.0;
const CGFloat LABEL_WIDTH = 210.0;
NSString * mediaUrl = [[[[self rssParser]rssItems]objectAtIndex:indexPath.row]mediaUrl];
NSData * imageData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:mediaUrl]];
UIImage * imageFromImageData;
if (imageData == nil) {
imageData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://www.urlForImage.image.png"]];
}
imageFromImageData = [[UIImage alloc] initWithData:imageData];
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"rssItemCell"];
if(nil == cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"rssItemCell"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UILabel * _topLabel =
[[UILabel alloc]
initWithFrame:
CGRectMake(
imageFromImageData.size.width + 10.0,
0.0,
LABEL_WIDTH,
LABEL_TITLE_HEIGHT)];
_topLabel.tag = kLabelTag;
_topLabel.opaque = NO;
_topLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;
_topLabel.backgroundColor = [UIColor clearColor];
_topLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
_topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
_topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
_topLabel.numberOfLines = 0;
[cell.contentView addSubview:_topLabel];
}
cell.imageView.image = imageFromImageData;
UILabel * topLabel = (UILabel *)[cell.contentView viewWithTag:1];
topLabel.text = [[[[self rssParser]rssItems]objectAtIndex:indexPath.row]title];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NewsDetailsViewController *tlc = [[DetailsViewController alloc]init];
tlc.currentItem = [[[self rssParser]rssItems]objectAtIndex:indexPath.row];
tlc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:tlc animated:YES completion:nil];
}
#end
I could not get a conclusion about the problem you are facing.
But here are few things you should check.
Because i cannot see even an empty table view in your screenshot
Do you have the TableView on the Nib file ?
It is mapped from The Nib file to the IBOutlet itemTableView ?
Add at least one table view cell (Drag and drop one prototype cell)..
like this
Then select that cell and give some name in "Reuse Identifier" with this identifier allow datasource..
First thing I would do is make sure that [[[self rssParser] rssItems] count] is actually returning > 0. Also, is this a copy&paste of your .m file? viewDidLoad is missing the closing brace, but Xcode would catch that.
If I run Build and Analyze this loads without errors according to the Analyzer but the app crashes. If I remove the [myStates release]; analyzer complains about possible leak but the nib loads and runs just fine. MyStateList is a nib which has a pickerview inside that loads a plist if this helps. Please help.
Main TrialViewControllerViewController Implementation File
#import "TrialViewControllerViewController.h"
#import "MyStateList.h"
#implementation TrialViewControllerViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyStateList *myStates = [[MyStateList alloc] initWithNibName:#"MyStateList" bundle:nil];
[self.view addSubview:[myStates view]];
//[myStates release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[super dealloc];
}
#end
Here is what i am trying to load
MyStateList.h
#import <UIKit/UIKit.h>
#interface MyStateList : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
UIPickerView *pickerView;
NSMutableArray *statesArray; // Written array
NSDictionary *stateListDictionary,*stateListDictionaries;
NSArray *firstDisplayArray,*updateDisplayArray,*switchDisplayArray;
NSInteger dTag;
NSString *nott,*verify,*notes;
IBOutlet UILabel *labelOne,*labelTwo,*labelThree,*labelName;
}
#property (nonatomic, retain) UIPickerView *pickerView;
#property (nonatomic, retain) NSDictionary *stateListDictionary,*stateListDictionaries;
#property (nonatomic, retain) NSArray *firstDisplayArray,*updateDisplayArray,*switchDisplayArray;
#property (nonatomic, retain) IBOutlet UILabel *labelOne,*labelTwo,*labelThree,*labelName;
#property (nonatomic, retain) NSString *nott,*verify,*notes;
- (void)loadData;
- (void)placeData;
- (void)createPicker;
#end
MyStateList.m
#import "MyStateList.h"
#implementation MyStateList
#synthesize pickerView;
#synthesize stateListDictionary,stateListDictionaries,firstDisplayArray,updateDisplayArray,switchDisplayArray ;
#synthesize labelOne,labelTwo,labelThree,labelName;
#synthesize nott,verify,notes;
- (void)viewDidLoad
{
[super viewDidLoad];
[self createPicker];
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [statesArray count];
}
-(UIView *)pickerView:(UIPickerView *)pickerViewCust viewForRow:(NSInteger)row forComponent: (NSInteger)component reusingView:(UIView *)view
{
NSString *rowItem = [statesArray objectAtIndex: row];
UILabel *lblRow = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, [pickerViewCust bounds].size.width, 44.0f)]autorelease];
[lblRow setTextAlignment:UITextAlignmentCenter];
[lblRow setTextColor: [UIColor blueColor]];
[lblRow setText:rowItem];
[lblRow setBackgroundColor:[UIColor clearColor]];
return lblRow;
}
- (void)createPicker
{
NSString *path = [[NSBundle mainBundle] pathForResource:
#"StateArray" ofType:#"plist"];
stateListDictionary = [NSDictionary dictionaryWithContentsOfFile:path];
labelName.text = [NSString stringWithFormat:#"Arizona"];
[self loadData];
float screenWidth = [UIScreen mainScreen].bounds.size.width;
float pickerWidth = screenWidth * 1 / 2;
float xPoint = screenWidth / 2 - pickerWidth / 1;
pickerView = [[UIPickerView alloc] init];
[pickerView setDataSource: self];
[pickerView setDelegate: self];
[pickerView setFrame: CGRectMake(xPoint, 280.0f, pickerWidth, 180.0f)];
pickerView.showsSelectionIndicator = YES;
[pickerView selectRow:2 inComponent:0 animated:YES];
[self.view addSubview: pickerView];
}
- (void)loadData
{
firstDisplayArray = [stateListDictionary objectForKey:#"Arizona"];
dTag = 1;
[self placeData];
stateListDictionary = nil; // kill the list
statesArray = [[NSMutableArray alloc] init];
[statesArray addObject:#"Alabama"];
[statesArray addObject:#"Alaska"];
[statesArray addObject:#"Arizona"];
[statesArray addObject:#"Arkansas"];
[statesArray addObject:#"California"];
}
- (void)placeData
{
if (dTag == 1)
{
switchDisplayArray = firstDisplayArray;
dTag = 0;
} else {
switchDisplayArray = updateDisplayArray;
}
nott = [switchDisplayArray objectAtIndex:0];
verify = [switchDisplayArray objectAtIndex:1];
notes = [switchDisplayArray objectAtIndex:2];
labelOne.text = nott;
labelTwo.text = verify;
labelThree.text = notes;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent: (NSInteger)component
{
NSString *path = [[NSBundle mainBundle] pathForResource:
#"StateArray" ofType:#"plist"];
stateListDictionaries = [NSDictionary dictionaryWithContentsOfFile:path];
labelName.text = [statesArray objectAtIndex: row];
updateDisplayArray = [stateListDictionaries objectForKey:labelName.text];
[self placeData];
}
- (void)dealloc
{
[pickerView release];
[stateListDictionary release];
[stateListDictionaries release];
[statesArray release];
[super dealloc];
}
#end
If I had to guess, MyStatesList is a subclass of UIViewController.
If you release myStates at the end of the block, you're essentially removing the brain from your view controller, but leaving its body there. You need to keep the controller itself around as well as the view, as the view is owned by the controller. Not the other way around.
The view from your view controller is retained by the view, but you killed the view controller itself. The bigger problem is you CAN'T just add views from UIViewControllers in this manner. On iOS5 you have the ability to use addChildViewController: and prior to that you can use one of the provided container controllers.
You do indeed need to release your MyStateList object.
Here's what's happening:
You initialize a new MyStateList, which has a retain count of 1
You add a subview to your view, and that subview is NOT the MyStateList object, but the ivar view of your MyStateList object.
When you don't release your myStates object, you are indeed leaking this object, since nothing has a reference to this object anymore, and the memory was never deallocated. (Its retain count never reached 0.)
That having been said, what kind of error occurs when your application crashes? I'm guessing that MyStateList is a UIViewController, and the view is then attempting to access/communicate with its parent, which has been released/deallocated.
In your -(void)dealloc; you are calling [super dealloc] first, this is wrong. Make it the last call in the method. This should fix your crash.
- (void)dealloc
{
// Wrong Way
[super dealloc];
[pickerView release];
[stateListDictionary release];
[stateListDictionaries release];
[statesArray release];
}
`
- (void)dealloc
{
// Right way
[pickerView release];
[stateListDictionary release];
[stateListDictionaries release];
[statesArray release];
[super dealloc];
}
you add the view [myStates view], to the controller's view, so the view is hold by the controller's view.
then you release "myStates" for avoid memeory leak, it is removed.
and I guess in your view [myStates view], there must be some delegates related to the object "myState", but it was removed just before. So, the app crash.
To avoid both problems, memory leak and crash. I think you should make the "myStates" hold by your controller as a member variable. And you could release "myState" when your viewUnloaded.
I have a table view controller in my app. Each cell is the name of a website. When a cell is hit a web view controller is created, passed a url and pushed to the view.
Pretty standard right?
If I go in and out of a web view enough times I begin to get memory warnings until the app crashes.
I can't see anywhere in my implementation where I'm not releasing anything properly. Is there some webview specific best practices or important delegate methods I might have left out?
This is driving me crazy because there is very little code involved and yet I still can't seem to find the problem.
Could somebody help me discover what is causing this memory problem or prehaps refer me to an article or tutorial so I can see how to do this properly?
Table View Controller
#implementation LinkListViewController
#synthesize linksArray;
#synthesize wvcontroller;
#pragma mark -
#pragma mark Initialization
- (void)loadView {
[super loadView];
table = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[self pushViewController:table animated:YES];
table.tableView.delegate = self;
table.tableView.dataSource = self;
table.title = #"Links"; // Tab Bar Title
[self.view addSubview:table.view];
}
-(id) initWithTabBar {
if ([self init]) {
self.tabBarItem.image = [UIImage imageNamed:#"events.png"];
}
return self;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [linksArray count];;
}
#define LABEL_TAG 7777
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LinkData *ld;
ld=[linksArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UILabel *label = nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
CGRect frame;
frame.origin.x = 0;
frame.origin.y = 10;
frame.size.width = 100;
frame.size.height = 30;
frame.origin.x = 0;
frame.size.width =290;
label = [[[UILabel alloc] initWithFrame:frame] autorelease];
label.tag = LABEL_TAG;
[cell.contentView addSubview:label];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
label = (UILabel *) [cell.contentView viewWithTag:LABEL_TAG];
}
label.text = [ld.linkname copy];
label.textColor=[UIColor blackColor];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
LinkData *ld=[linksArray objectAtIndex:indexPath.row];
if ([ld.linktype isEqualToString:#"webpage"]) {
if (wvcontroller) {
[wvcontroller release];
wvcontroller=nil;
}
wvcontroller= [[WebViewController alloc]initWithPath:ld.linkurl];
wvcontroller.title=ld.linkname;
[table.navigationController pushViewController:wvcontroller animated:YES];
}
}
Web View Controller
#import "WebViewController.h"
#implementation WebViewController
#synthesize webView;
#synthesize path;
#synthesize showsync;
#synthesize activity;
-(id)initWithPath:(NSString *)thepath
{
if ( [self init]) {
self.path= [thepath copy];
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
[self.view setBackgroundColor:[UIColor blackColor]];
[self loadView];
}
return self;
}
- (void)loadView {
//Create a URL object.
webView = [[UIWebView alloc] initWithFrame:self.view.frame];
webView.autoresizesSubviews = YES;
webView.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
//set the web view delegates for the web view to be itself
self.webView.scalesPageToFit = YES;
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.webView.delegate = self;
//Create a URL object.
NSURL *url = [NSURL URLWithString:path];
//URL Requst Object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//add the web view to the content view
[self.view addSubview:webView];
//load the URL into the web view.
[webView loadRequest:requestObj];
//[webView release], webView = nil;
activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activity.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activity.center = self.view.center;
[self.view addSubview: activity];
}
/*
If you need to do additional setup after loading the view, override viewDidLoad. */
- (void)viewDidLoad {
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.webView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[webView release];
[activity release];
webView=nil;
[path release];
[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.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[activity startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activity stopAnimating];
}
You still hold an extra reference to self.view at the end of initWithPath which should be released.
A bit more explanation: [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] gives you a UIView with a retain count of 1 (since you have just allocated it). Assigning it to self.view bumps its retain count by 1, so it is 2 at the moment. When your controller is deallocated, it takes care of decreasing the retain count of the object in self.view, but the count is still 1, so it never gets deallocated. You should do this:
UIView* myView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = myView;
[myView release];
One way to avoid UIWebView memory problems is to create only one webview one time, and reuse it for the whole life of the application. That is, not release it but reuse it.
The iOS app I'm currently working on is tabbar-based, and one of the tab is a UITableViewController.
The thing is, when I open this tab with an empty datasource (for whatever reason), I'd like to bring another view, with some kind of message/image, instead of the blank view I get with the tableviewcontroller.
I tried something like that :
- (void)viewWillAppear:(BOOL)animated {
if ([myData count] == 0) {
if (!emptyView) {
emptyView = [[UIView alloc] initWithFrame:self.view.frame];
UILabel *emptyMsg = [[UILabel alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, 20)];
emptyMsg.text = #"This is Empty !";
emptyMsg.textAlignment = UITextAlignmentCenter;
[emptyView addSubview:emptyMsg];
}
[self.view insertSubview:emptyView atIndex:0];
}
else {
if (emptyView != nil) { [emptyView removeFromSuperview]; emptyView = nil; }
[self.tableView reloadData];
[super viewWillAppear:animated];
}
}
With emptyView defined as an iVar in the view controller.
But It doesn't work as expected, and I can't find the reason :/
Could any of you give it a look and give me the proper way to do this kind of behavior ?
Thanks,
I solved this problem by adding a UIView in the tableview header with a frame size equal to tableview size and disallowing user interaction on the tableview:
UIView *emptyView = [[UIView alloc] initWithFrame:self.tableView.frame];
/* Customize your view here or load it from a NIB */
self.tableView.tableHeaderView = emptyView;
self.tableView.userInteractionEnabled = NO;
When you have data you just remove the header and reenable user interaction:
self.tableView.tableHeaderView = nil;
self.tableView.userInteractionEnabled = YES;
UITableViewController doesn't allow you to add subviews to it's view (the tableView).
You should make a UIViewController and add the UITableView yourself with your optional emptyView.
Don't forget to set the dataSource and the delegate!
Update : I've made a subclass of UIViewController to avoid mimics UITableViewController every time.
.h
//
// GCTableViewController.h
// GCLibrary
//
// Created by Guillaume Campagna on 10-06-17.
// Copyright 2010 LittleKiwi. All rights reserved.
//
#import <UIKit/UIKit.h>
//Subclass of UIViewController that mimicks the UITableViewController except that the tableView is a subview of self.view and allow change of the frame of the tableView
#interface GCTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, readonly) UITableView *tableView;
- (id) initWithStyle:(UITableViewStyle)style;
//Subclass if you want to change the type of tableView. The tableView will be automatically placed later
- (UITableView*) tableViewWithStyle:(UITableViewStyle) style;
#end
.m
//
// GCTableViewController.m
// GCLibrary
//
// Created by Guillaume Campagna on 10-06-17.
// Copyright 2010 LittleKiwi. All rights reserved.
//
#import "GCTableViewController.h"
#implementation GCTableViewController
#synthesize tableView;
- (id) initWithStyle:(UITableViewStyle) style {
if (self = [super initWithNibName:nil bundle:nil]) {
tableView = [[self tableViewWithStyle:style] retain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
self.tableView.frame = self.view.bounds;
self.tableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
#pragma mark TableView methods
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger) tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
return 0;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
#pragma mark Getter
- (UITableView *) tableViewWithStyle:(UITableViewStyle)style {
return [[[UITableView alloc] initWithFrame:CGRectZero style:style] autorelease];
}
- (void)dealloc {
[tableView release];
tableView = nil;
[super dealloc];
}
#end
You could perhaps try setting the number of rows to zero. Then inserting the no results view as the header view.
I have a tableView:didSelectRowAtIndexPath: where I create a ViewController-Instance each time an item is selected. Sometimes it works fine for a long time, sometimes it crashes with a EXC_BAD_ACCESS very quickly. The debugger blames the line [super dealloc]; in the QuestionDetailViewController.
Why? I log the QuestionDetailViewController retainCount. That looks fine.
QuestionDetailViewController
#import <UIKit/UIKit.h>
#import "Question.h"
#import "Answer.h"
#interface QuestionDetailViewController : UIViewController < UIScrollViewDelegate , QuestionDetailViewProtocol> {
Question *question;
UILabel *answerText;
UILabel *titleLabel;
}
#property(nonatomic,retain) Question *question;
#property(nonatomic,retain) UILabel *answerText;
#property(nonatomic,retain) UILabel *titleLabel;
#end
#import "QuestionDetailViewController.h"
#implementation QuestionDetailViewController
#synthesize question;
#synthesize answerText;
#synthesize titleLabel;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
UIColor *bgColor = [UIColor colorWithRed:199.0/255 green:234.0/255 blue:251.0/255 alpha:1];
self.answerText = (UILabel *)[self.view viewWithTag:2];
self.answerText.backgroundColor = bgColor;
self.answerText.text = self.question.answer.text;
self.titleLabel = (UILabel*)[self.view viewWithTag:1];
CGSize sizeTitle = [self.question.title sizeWithFont:[titleLabel font]
constrainedToSize:CGSizeMake(280.0, INFINITY)
lineBreakMode:UILineBreakModeWordWrap];
self.titleLabel.text = self.question.title;
self.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
self.titleLabel.frame = CGRectMake(10,10, 260, sizeTitle.height);
CGSize sizeText = [self.question.answer.text sizeWithFont:[self.answerText font]
constrainedToSize:CGSizeMake(280.0, INFINITY)
lineBreakMode:UILineBreakModeWordWrap];
self.answerText.frame = CGRectMake(0,20+sizeTitle.height, 310, sizeText.height+sizeTitle.height);
[(UIScrollView *)self.view setContentSize:CGSizeMake(280, answerText.frame.size.height +sizeTitle.height)];
self.view.backgroundColor = bgColor;
[super viewWillAppear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(#"%d", [self retainCount]);
[super viewDidDisappear:animated];
[self release];
}
-(IBAction)goBack:(id)sender{
[self.navigationController popViewControllerAnimated:YES];
}
- (void)dealloc {
NSLog(#"QDC dealoc");
[self.answerText release];
[self.titleLabel release];
[self.question release];
[super dealloc];
}
#end
tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
QuestionDetailViewController *qdc = [[QuestionDetailViewController alloc] initWithNibName:#"QuestionDetailViewController" bundle:nil];
Question* question;
if ([filteredQuestions count]>0) {
question = [self.filteredQuestions objectAtIndex:indexPath.row];
} else {
question = [self.questions objectAtIndex:indexPath.row];
}
[qdc setQuestion:question];
[question release];
NSLog(#"%d", [qdc retainCount]);
[self.navigationController pushViewController:qdc animated:YES];
[qdc release];
}
This:
- (void)viewDidDisappear:(BOOL)animated {
NSLog(#"%d", [self retainCount]);
[super viewDidDisappear:animated];
[self release];
}
looks incredibly suspicious. Did you do [self retain]? If not, then why are you releasing? If you are, you're probably doing something wrong, because [self retain] is an incredibly rare thing to need to do.
Instead of:
[self.answerText release];
[self.titleLabel release];
[self.question release];
use:
self.answerText = nil;
self.titleLabel = nil;
self.question = nil;
or:
[answerText release]; answerText = nil;
[titleLabel release]; titleLabel = nil;
[question release]; question = nil;