Passing Data from Detail View to Master View (Splitviewcontroller) - ios

i'm really new to iOS programming and i have a problem with the Splitviewcontroller.
I'm programming an iPad App, that load a picture from the gallery and then save the EXIF Data into a Table View. Loading the picture is no problem, but when i'm trying to save the EXIF Data into the Table View, nothing happen. I have the same App for the iPhone but not with the Splitview and there everything works.
I searched for days now for an answer, but nothing was really helpful for my problem.
Here is some code:
ViewController.m
#import "ViewController-iPad.h"
#import "TableController-iPad.h"
#implementation ViewController_iPad
{
UIPopoverController *masterPopoverController;
}
#synthesize exifPtr;
#synthesize toolbar;
#synthesize imageView;
#synthesize detailItem = _detailItem;
-(void) setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem)
{
_detailItem = newDetailItem;
}
if (masterPopoverController != nil)
{
[masterPopoverController dismissPopoverAnimated: YES];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Device has no camera"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[myAlertView show];
}
}
- (void)viewDidUnload
{
[self setToolbar:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (IBAction)selectPhoto:(UIButton *)sender
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"exifCell"])
{
TableController_iPad *controller = (TableController_iPad *)segue.destinationViewController;
controller.exifPtr = exifPtr;
}
}
+ (ALAssetsLibrary *)defaultAssetsLibrary
{
static dispatch_once_t pred = 0;
static ALAssetsLibrary *library = nil;
dispatch_once(&pred, ^{
library = [[ALAssetsLibrary alloc] init];
});
return library;
}
#pragma mark - Image Picker Controller delegate methods
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
self.imageView.image = chosenImage;
NSURL *url = info[UIImagePickerControllerReferenceURL];
ALAssetsLibrary *lib = [ViewController_iPad defaultAssetsLibrary];
[lib assetForURL:url resultBlock:^(ALAsset *asset)
{
// get data
ALAssetRepresentation *repr = [asset defaultRepresentation];
long long bytes = [repr size];
unsigned char *dataPtr = malloc((unsigned long)bytes);
[repr getBytes:dataPtr fromOffset:0 length:(NSUInteger)bytes error:NULL];
JPEG_GetEXIFFromMemory(dataPtr, (long)bytes, &exifPtr);
PrvConvertEXIFToText(exifPtr);
free(dataPtr);
} failureBlock:^(NSError *error)
{
;
}];
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker dismissViewControllerAnimated:YES completion:NULL];
}
// Standard-Methoden für einen SplitViewController
-(void) splitViewController:(UISplitViewController *)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)pc
{
barButtonItem.title = #"EXIF";
self.navigationItem.rightBarButtonItem = barButtonItem;
NSMutableArray *items = [[self.toolbar items] mutableCopy];
[items insertObject: barButtonItem atIndex: 0];
[self.toolbar setItems:items animated:YES];
masterPopoverController = pc;
}
-(void) splitViewController:(UISplitViewController *)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
NSMutableArray *items = [[self.toolbar items] mutableCopy];
[items removeObject:barButtonItem];
[self.toolbar setItems:items animated:YES];
masterPopoverController = nil;
}
#end
TableController.m
#import "TableController-iPad.h"
#interface TableController_iPad()
#property (nonatomic, strong) NSArray *exifTitleArray;
#property (nonatomic, strong) NSArray *exifContentArray;
#end
#implementation TableController_iPad
#synthesize exifTitle;
#synthesize exifContent;
#synthesize exifPtr;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
cellTitle = [NSArray arrayWithObjects: exifTitle, nil];
cellContent = [NSArray arrayWithObjects: exifContent, nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(exifPtr==0)
{
return 0;
}
return exifPtr->textCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSString *simpleTableIdentifier = [NSString stringWithFormat:#"exifCell %ld", (long)indexPath.row];
static NSString *simpleTableIdentifier = #"exifCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:simpleTableIdentifier];
}
// Configure the cell...
char * exifDataTitle = exifPtr->exifTextRec[indexPath.row].description;
NSString * exifCStringTitle = [NSString stringWithCString: exifDataTitle encoding:NSUTF8StringEncoding];
char * exifDataContent = exifPtr->exifTextRec[indexPath.row].content;
NSString * exifCStringContent = [NSString stringWithCString: exifDataContent encoding:NSUTF8StringEncoding];
cell.textLabel.font = [UIFont systemFontOfSize:10.0];
cell.textLabel.text = exifCStringTitle;
cell.detailTextLabel.font = [UIFont systemFontOfSize:10.0];
cell.detailTextLabel.text = exifCStringContent;
return cell;
}
#end
PS: Sorry for some grammar mistakes, english is not my best language ;)

You can use custom delegate to get data from child view controller to parent view controller.
This link is may help you to achieve this.

Related

how to use ABRecordGetRecordID to fill up a table with contactinformation

I have a sqlite database where i store ABRecordGetRecordID's of a contact.
I convert the ABRecordGetRecordID's to an NSNumber to store it in the database:
2014-05-21 14:35:12.122 ProjectMobile[17858:60b] 179962464 <-- this is the kind of number i get when i log the following:
#import "HulpViewcontroller.h"
#import "AidTableViewcell.h"
#property UIColor * firstColor;
#property UIColor * secondColor;
#property NSMutableArray *Personen;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation HulpViewController
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.Personen count];
}
- (IBAction)pickPerson:(id)sender {
ABPeoplePickerNavigationController *picker =
[[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
- (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker {
[[peoplePicker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
// Create a new person
NSManagedObject *newPersoon = [NSEntityDescription insertNewObjectForEntityForName:#"Personen" inManagedObjectContext:self.context];
NSNumber *id = [NSNumber numberWithInt:ABRecordGetRecordID(person)];
NSNumber *one = [NSNumber numberWithInt:1];
NSLog(#"%d", id);
[newPersoon setValue: id forKey:#"persoonId"];
[newPersoon setValue: one forKey:#"importance"];
NSError *error = nil;
// Save the object to persistent store
if (![self.context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[[peoplePicker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
return NO;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
return NO;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"hulpCell";
AidTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[AidTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// check if row is odd or even and set color accordingly
if (indexPath.row % 2) {
cell.backgroundColor = self.firstColor;
}else {
cell.backgroundColor = self.secondColor;
}
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.firstColor = [UIColor colorWithRed:183/255.0f green:80/255.0f blue:23/255.0f alpha:1.0f];
self.secondColor = [UIColor colorWithRed:179/255.0f green:98/255.0f blue:0/255.0f alpha:1.0f];
self.Personen = [[NSMutableArray alloc]initWithObjects:#"Man.jpeg", #"Doctor.jpeg", #"Son.jpeg", nil];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self context];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Personen"];
self.personen = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
How can i use the Id i stored in the database to put information of the contacts of those id's in a tableview?
I updated the code. This way it will be easier for you guys to help me out :)
Thanks!
You could e.g. do something like this:
CFErrorRef error = nil;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions (NULL, &error);
if (addressBookRef != nil) {
ABRecordID recordID = yourPersonRecordID; // Assign here your ID
ABRecordRef nxtABRecordRef = ABAddressBookGetPersonWithRecordID (addressBookRef, recordID);
// Do anything with your record, e.g.
ABMultiValueRef addressesRef = ABRecordCopyValue(nxtABRecordRef, kABPersonAddressProperty);
if (addressesRef != nil) {
for (int index = 0; index < ABMultiValueGetCount(addressesRef); index++) {
NSDictionary *addressDictionary = (__bridge_transfer NSDictionary*) ABMultiValueCopyValueAtIndex(addressesRef,index);
// Do something with the persons data
} // for all addresses
CFRelease(addressesRef);
}
CFRelease(addressBookRef);
} else {
NSLog(#"Could not open address book");
}

How to implement MWFeedParser in my project

I'm trying to use the MWFeedParser library in my app. On my homescreen, I have a view controller named NewsViewController.
In the MWFeedParser library, the root view controller is called RootViewController. I've tried to copy all the code from the RootViewController into the NewsViewController .H + .M and in IB I've linked the tableview to "dataSource" and "delegate". But when my app starts the tableview is empty.
Here's how to code looks like:
.H:
#import <UIKit/UIKit.h>
#import "MWFeedItem.h"
#import "MWFeedParser.h"
#interface NewsViewController : UITableViewController <MWFeedParserDelegate, UITableViewDelegate, UITableViewDataSource> {
// Parsing
MWFeedParser *feedParser;
NSMutableArray *parsedItems;
// Displaying
NSArray *itemsToDisplay;
NSDateFormatter *formatter;
IBOutlet UITableView *tableView;
}
// Properties
#property (nonatomic, retain) NSArray *itemsToDisplay;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
-(IBAction)goHome;
#end
.M:
#import "NSString+HTML.h"
#import "MWFeedParser.h"
#import "DetailTableViewController.h"
#implementation NewsViewController
#synthesize itemsToDisplay, tableView;
#pragma mark -
#pragma mark View lifecycle
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"News", #"News");
self.tabBarItem.image = [UIImage imageNamed:#"icon_news"]; }
return self;
}
- (void)viewDidLoad
{
label.shadowOffset = CGSizeMake(0.0f, 1.0f);
label.textColor = [UIColor colorWithRed:0xB3/249.0 green:0xB3/252.0 blue:0xB3/253.0 alpha:1];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// Date
// Setup
formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
parsedItems = [[NSMutableArray alloc] init];
self.itemsToDisplay = [NSArray array];
// Refresh button
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
target:self
action:#selector(refresh)];
// Parse
NSURL *feedURL = [NSURL URLWithString:#"http://www.mywebsite.com/feed/"];
feedParser = [[MWFeedParser alloc] initWithFeedURL:feedURL];
feedParser.delegate = self;
feedParser.feedParseType = ParseTypeFull; // Parse feed info and all items
feedParser.connectionType = ConnectionTypeAsynchronously;
[feedParser parse];
UIImage *someImage = [UIImage imageNamed:#"back_active1#2x.png"];
[button setBackgroundImage:someImage forState:UIControlStateHighlighted];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidAppear:(BOOL)animated {
static BOOL first = YES;
if (first) {
UIViewController *popup = [[Home1ViewController alloc] initWithNibName:#"Home1ViewController" bundle:nil];
[self presentViewController:popup animated:NO completion:nil];
first = NO;
}
}
#pragma mark -
#pragma mark Parsing
// Reset and reparse
- (void)refresh {
self.title = #"Refreshing...";
[parsedItems removeAllObjects];
[feedParser stopParsing];
[feedParser parse];
self.tableView.userInteractionEnabled = NO;
self.tableView.alpha = 0.3;
}
- (void)updateTableWithParsedItems {
self.itemsToDisplay = [parsedItems sortedArrayUsingDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"date"
ascending:NO]]];
self.tableView.userInteractionEnabled = YES;
self.tableView.alpha = 1;
[self.tableView reloadData];
}
#pragma mark -
#pragma mark MWFeedParserDelegate
- (void)feedParserDidStart:(MWFeedParser *)parser {
NSLog(#"Started Parsing: %#", parser.url);
}
- (void)feedParser:(MWFeedParser *)parser didParseFeedInfo:(MWFeedInfo *)info {
NSLog(#"Parsed Feed Info: “%#”", info.title);
self.title = info.title;
}
- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item {
NSLog(#"Parsed Feed Item: “%#”", item.title);
if (item) [parsedItems addObject:item];
}
- (void)feedParserDidFinish:(MWFeedParser *)parser {
NSLog(#"Finished Parsing%#", (parser.stopped ? #" (Stopped)" : #""));
[self updateTableWithParsedItems];
}
- (void)feedParser:(MWFeedParser *)parser didFailWithError:(NSError *)error {
NSLog(#"Finished Parsing With Error: %#", error);
if (parsedItems.count == 0) {
self.title = #"Failed"; // Show failed message in title
} else {
// Failed but some items parsed, so show and inform of error
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Parsing Incomplete"
message:#"There was an error during the parsing of this feed. Not all of the feed items could parsed."
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alert show];
}
[self updateTableWithParsedItems];
}
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return itemsToDisplay.count;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Configure the cell.
MWFeedItem *item = [itemsToDisplay objectAtIndex:indexPath.row];
if (item) {
// Process
NSString *itemTitle = item.title ? [item.title stringByConvertingHTMLToPlainText] : #"[No Title]";
NSString *itemSummary = item.summary ? [item.summary stringByConvertingHTMLToPlainText] : #"[No Summary]";
// Set
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.textLabel.text = itemTitle;
NSMutableString *subtitle = [NSMutableString string];
if (item.date) [subtitle appendFormat:#"%#: ", [formatter stringFromDate:item.date]];
[subtitle appendString:itemSummary];
cell.detailTextLabel.text = subtitle;
}
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Show detail
DetailTableViewController *detail = [[DetailTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
detail.item = (MWFeedItem *)[itemsToDisplay objectAtIndex:indexPath.row];
[self.navigationController pushViewController:detail animated:YES];
// Deselect
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#end
Please help me fix this!
you can follow the following link to use MWFeedParser : https://0club1.blogspot.in/
If your Table view is empty, you could have got the following error:
NSURLSession/NSURLConnection HTTP load failed
To allow HTTP, we need to allow arbitrary loads in App Transport Security Settings. Select info.plist in your project, in Information property list add new list as App Transport Security Settings.
Within that add Allow Arbitary Loads and mark it as YES.

Master-Detail Split View

I have an iPhone app that I'm wanting to make a universal app and implement a split view on the iPad. I have the master view set up so that when the user selects a row, the master view goes to another table view. When a row is selected in that table view, I want the detail view to show my web view. This all works but the issue I'm having is when the web view shows on the detail view, it is just getting added to the navigation stack instead of just replacing what is currently there. This makes it so that the user has to press the back button on the detail view navigation bar in order to make another selection in the master view. If another selection is made in the master view without pressing the back button on the detail view, the app crashes.
I need to figure out how to have the web view not get added to the navigation stack of the detail view and instead replace the current detail view.
MasterViewController:
#import "KFBMasterViewController.h"
#import "KFBDetailViewController.h"
#import "ListViewController.h"
#import "SocialNetworks.h"
#import "WebViewController.h"
#interface KFBMasterViewController () {
NSMutableArray *_objects;
NSMutableArray *menu;
}
#end
#implementation KFBMasterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Master", #"Master");
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// self.navigationItem.leftBarButtonItem = self.editButtonItem;
// UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
// self.navigationItem.rightBarButtonItem = addButton;
menu = [NSMutableArray arrayWithObjects:#"Home", #"Public Affairs", #"Action Alerts", #"Market Updates", #"Ag Stories", #"KFB News", #"Member Benefits", #"Monthly Video", #"Photos", #"Social Media", #"About Us", #"Contact Us", #"KYFB.com", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return menu.count;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// NSDate *object = _objects[indexPath.row];
// cell.textLabel.text = [object description];
cell.textLabel.text = [menu objectAtIndex:indexPath.row];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_objects removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// NSDate *object = _objects[indexPath.row];
// self.detailViewController.detailItem = object;
// KFBDetailViewController *detailViewController = (KFBDetailViewController*)self.splitViewController.delegate;
// UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
if (indexPath.row == 1)
{
// [((UINavigationController *)[self.splitViewController.viewControllers lastObject]) pushViewController:[[ListViewController alloc]initWithStyle:UITableViewStylePlain] animated:YES];
// [[self.splitViewController.viewControllers lastObject] pushViewController:[[ListViewController alloc]initWithStyle:UITableViewStylePlain] animated:YES];
// ListViewController *publicAffairs = [[ListViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];
// [self.navigationController pushViewController:publicAffairs animated:YES];
ListViewController *publicAffairs = [[ListViewController alloc]initWithStyle:UITableViewStylePlain];
WebViewController *wvc = [[WebViewController alloc]init];
[publicAffairs setWebViewController:wvc];
publicAffairs.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self.navigationController pushViewController:publicAffairs animated:YES];
// [self presentViewController:publicAffairs animated:YES completion:nil];
}
else if (indexPath.row == 9)
{
// [((UINavigationController *)[self.splitViewController.viewControllers lastObject]) pushViewController:[[SocialNetworks alloc]initWithNibName:#"SocialNetworks" bundle:nil] animated:YES];
// [[self.splitViewController.viewControllers lastObject] pushViewController:[[SocialNetworks alloc]initWithNibName:#"SocialNetworks" bundle:nil] animated:YES];
SocialNetworks *social = [[SocialNetworks alloc] initWithNibName:#"SocialNetworks" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:social animated:YES];
}
}
#end
DetailViewController:
#import "KFBDetailViewController.h"
#interface KFBDetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
#end
#implementation KFBDetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Detail", #"Detail");
}
return self;
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(#"Master", #"Master");
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
#end
ListViewController:
#import "ListViewController.h"
#import "RSSChannel.h"
#import "RSSItem.h"
#import "WebViewController.h"
#import "KFBDetailViewController.h"
// #import "CustomCellBackground.h"
#implementation ListViewController
{
UIActivityIndicatorView *loadingIndicator;
}
#synthesize webViewController;
- (void)viewDidLoad
{
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
self.title = #"Public Affairs";
loadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
loadingIndicator.center = CGPointMake(160, 160);
loadingIndicator.hidesWhenStopped = YES;
[self.view addSubview:loadingIndicator];
[loadingIndicator startAnimating];
// UIRefreshControl *refresh = [[UIRefreshControl alloc] init];
// refresh.attributedTitle = [[NSAttributedString alloc] initWithString:#"Pull to Refresh"];
// [refresh addTarget:self action:#selector(refreshView:)forControlEvents:UIControlEventValueChanged];
// self.refreshControl = refresh;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqual:#"channel"])
{
// If the parser saw a channel, create new instance, store in our ivar
channel = [[RSSChannel alloc]init];
// Give the channel object a pointer back to ourselves for later
[channel setParentParserDelegate:self];
// Set the parser's delegate to the channel object
[parser setDelegate:channel];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// return 0;
NSLog(#"channel items %d", [[channel items]count]);
return [[channel items]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// return nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
cell.textLabel.font=[UIFont systemFontOfSize:16.0];
}
RSSItem *item = [[channel items]objectAtIndex:[indexPath row]];
// cell.backgroundView = [[CustomCellBackground alloc] init];
// cell.selectedBackgroundView = [[CustomCellBackground alloc] init];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.highlightedTextColor = [UIColor darkGrayColor];
if (item.isActionAlert) {
NSLog(#"Action Alert...Not Using");
}
else
{
[[cell textLabel]setText:[item title]];
}
return cell;
}
- (void)fetchEntries
{
// Create a new data container for the stuff that comes back from the service
xmlData = [[NSMutableData alloc]init];
// Construct a URL that will ask the service for what you want -
NSURL *url = [NSURL URLWithString:#"http://kyfbnewsroom.com/category/public-affairs/feed"];
// Put that URL into an NSURLRequest
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Create a connection that will exchange this request for data from the URL
connection = [[NSURLConnection alloc]initWithRequest:req delegate:self startImmediately:YES];
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self)
{
[self fetchEntries];
}
return self;
}
// This method will be called several times as the data arrives
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
// Add the incoming chunk of data to the container we are keeping
// The data always comes in the correct order
[xmlData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn
{
/* We are just checking to make sure we are getting the XML
NSString *xmlCheck = [[NSString alloc]initWithData:xmlData encoding:NSUTF8StringEncoding];
NSLog(#"xmlCheck = %#", xmlCheck);*/
[loadingIndicator stopAnimating];
// Create the parser object with the data received from the web service
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
// Give it a delegate
[parser setDelegate:self];
//Tell it to start parsing - the document will be parsed and the delegate of NSXMLParser will get all of its delegate messages sent to it before this line finishes execution - it is blocking
[parser parse];
// Get rid of the XML data as we no longer need it
xmlData = nil;
// Reload the table.. for now, the table will be empty
NSMutableArray *actionAlerts = [NSMutableArray array];
for (RSSItem *object in channel.items) {
if (object.isActionAlert) {
[actionAlerts addObject:object];
}
}
for (RSSItem *object in actionAlerts) {
[channel.items removeObject:object];
}
[[self tableView]reloadData];
NSLog(#"%#\n %#\n %#\n", channel, [channel title], [channel infoString]);
}
- (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error
{
// Release the connection object, we're done with it
connection = nil;
// Release the xmlData object, we're done with it
xmlData = nil;
// Grab the description of the error object passed to us
NSString *errorString = [NSString stringWithFormat:#"Fetch failed: %#", [error localizedDescription]];
// Create and show an alert view with this error displayed
UIAlertView *av = [[UIAlertView alloc]initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[av show];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[((UINavigationController *) [self.splitViewController.viewControllers lastObject]) pushViewController:webViewController animated:YES];
// Grab the selected item
RSSItem *entry = [[channel items]objectAtIndex:[indexPath row]];
NSLog(#"Channel Items: %#", [[channel items]objectAtIndex:[indexPath row]]);
// Construct a URL with the link string of the item
NSURL *url = [NSURL URLWithString:[entry link]];
NSLog(#"Link: %#", [entry link]);
// Construct a request object with that URL
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSLog(#"URL: %#", url);
// Load the request into the web view
[[webViewController webView]loadRequest:req];
webViewController.hackyURL = url;
NSLog(#"Request: %#", req);
// Set the title of the web view controller's navigation item
// [[webViewController navigationItem]setTitle:[entry title]];
NSLog(#"Title: %#", [entry title]);
}
#end
AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
KFBMasterViewController *masterViewController = [[KFBMasterViewController alloc] initWithNibName:#"KFBMasterViewController" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
KFBDetailViewController *detailViewController = [[KFBDetailViewController alloc] initWithNibName:#"KFBDetailViewController" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
masterViewController.detailViewController = detailViewController;
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = #[masterNavigationController, detailNavigationController];
self.window.rootViewController = self.splitViewController;
[self.window makeKeyAndVisible];
return YES;
}

Memory management in viewDidUnload - should I nil both array and objects inside?

I have a tableview to load news from internet. I and trying to nil all properties in viewDidUnload.
- (void)viewDidUnload
{
self.newsArray = nil;
self.newsTableView = nil;
self.indicatorView = nil;
// self.iconDownLoader = nil;
self.downloadArray = nil;
[super viewDidUnload];
}
Every time the app crash in viewDidUnload. If I comment self.iconDownLoader = nil;, it will be fine. So can any one tell me why does this happen? Thanks you.
---------------------NewsViewController.m--------------------------
//
// NewsViewController.m
//
// Created by on 18/01/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "NewsViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"
#import "NewsModel.h"
#import "NewsDetailViewController.h"
#define kCustomRowCount 6
#define IconPlaceHolder #"Spinner"
#implementation NewsViewController
#synthesize appDelegate, newsTableViewCell, newsTableView, indicatorView;
#synthesize iconDownLoader, newsArray, downloadArray;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// setup appDelegate
self.appDelegate = (SydneyAppDelegate *)[[UIApplication sharedApplication] delegate];
// initial arrays
self.newsArray = [[NSMutableArray alloc] init];
self.downloadArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
if(self.appDelegate.reachable) {
[self getNews];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Connection" message:#"No Internet connection. Please try again later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
- (void)viewDidUnload
{
self.newsArray = nil;
self.newsTableView = nil;
self.indicatorView = nil;
// self.iconDownLoader = nil;
self.downloadArray = nil;
[super viewDidUnload];
}
#pragma mark - ASIHTTPRequest
- (void) getNews
{
NSURL *url = [NSURL URLWithString:#"http://ferrarimaseratisydney.com/api/getPublicNews.html"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
- (void) requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
NSArray *json = [responseString JSONValue];
for (id aNewsInJson in json)
{
NewsModel *aNews = [[NewsModel alloc] initWithJson:aNewsInJson];
[self.newsArray addObject:aNews];
}
[self.indicatorView removeFromSuperview];
[self.newsTableView reloadData];
}
- (void) requestFailed:(ASIHTTPRequest *)request
{
NSError *error;
error = [request error];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Navigation logic may go here. Create and push another view controller.
NewsDetailViewController *newsDetailViewController = [[NewsDetailViewController alloc] init];
// transform news array
newsDetailViewController.news = [self.newsArray objectAtIndex:indexPath.row];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:newsDetailViewController animated:YES];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.newsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NewsCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NewsTableViewCell" owner:self options:nil];
cell = self.newsTableViewCell;
self.newsTableViewCell = nil;
}
// read from newsModel
NewsModel *news = [self.newsArray objectAtIndex:indexPath.row];
UILabel *label;
label = (UILabel *)[cell viewWithTag:10];
label.text = [NSString stringWithString:news.title];
label = nil;
label = (UILabel *)[cell viewWithTag:11];
label.text = [NSString stringWithString:news.description];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
imageView.image = news.image;
if (news.image == nil)
{
imageView.image = [UIImage imageNamed:IconPlaceHolder];
self.iconDownLoader = [[IconDownLoader alloc] init];
self.iconDownLoader.url = news.imageUrl;
self.iconDownLoader.delegate = self;
self.iconDownLoader.indexPath = indexPath;
if (self.appDelegate.ip4 == YES)
{
self.iconDownLoader.width = 300;
self.iconDownLoader.height = 150;
}
else
{
self.iconDownLoader.width = 150;
self.iconDownLoader.height = 75;
}
[self.downloadArray addObject:self.iconDownLoader];
[self.iconDownLoader start];
}
return cell;
}
#pragma mark - IconDownLoaderDelegate
- (void)iconDownLoadFinsh:(NSData *)imageData row:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.newsTableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
if (imageData != 0)
{
imageView.image = [UIImage imageWithData:imageData];
}
else
{
imageView.image = [UIImage imageNamed:#"icon57"];
}
NewsModel *newsModel = [self.newsArray objectAtIndex:indexPath.row];
newsModel.image = [UIImage imageWithData:imageData];
}
#end
-----------------------IconDownLoader.m-------------------
//
// IconDownLoader.m
//
// Created by on 24/11/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "IconDownLoader.h"
#import "ASIHTTPRequest.h"
#implementation IconDownLoader
#synthesize delegate = _delegate;
#synthesize url = _url;
#synthesize indexPath = _indexPath;
#synthesize width = _width;
#synthesize height = _height;
#synthesize request = _request;
- (void)start {
NSString *originalString = #"width=%s&height=%s";
NSString *newString = [NSString stringWithFormat:#"width=%d&height=%d&type=jpg", self.width, self.height];
NSString *resizedURL = [self.url stringByReplacingOccurrencesOfString:originalString withString:newString];
NSURL *url = [NSURL URLWithString:[resizedURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
_request = [ASIHTTPRequest requestWithURL:url];
if (_indexPath) {
_request.userInfo = [NSDictionary dictionaryWithObject:_indexPath forKey:#"indexPath"];
}
[_request setDelegate:self];
[_request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
NSInteger statusCode = request.responseStatusCode;
switch (statusCode) {
case 401: // Not Authorized: either you need to provide authentication credentials, or the credentials provided aren't valid.
break;
case 200: {
NSData *responseData = [request responseData];
if (!responseData) {
UIAlertView *alertView;
alertView = [[UIAlertView alloc] initWithTitle:#"Oops" message:[NSString stringWithFormat:#"Download failed in row %d", _indexPath.row] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
return;
}
[_delegate iconDownLoadFinsh:responseData row:[request.userInfo objectForKey:#"indexPath"]];
}
break;
default:{
}
}
}
- (void)dealloc {
if (_request != nil) {
[_request clearDelegatesAndCancel];
}
}
#end
Generally in viewDidUnload you should only release and zero out all references to the nib objects you own.
That said, you can destroy model objects in viewDidUnload too if they consume a lot of memory. You should remember that viewDidUnload is a counterpart to viewDidLoad, so a good rule of thumb is to destroy only those objects in viewDidUnload which you create in viewDidLoad. You should also remember that viewDidUnload is not called when the view controller is released – only when its view is.
In your case I would not release newsArray and downloadArray just because you create them in init.... I would just send them removeAllObjects instead.
As for the crash, you create a new shared icon downloader every time a cell needs an image, which is a bit awkward. If you do need a shared downloader instance, you should not recreate it for each and every cell. It looks like you want to keep all the downloaders you created in an ivar array instead because each downloader is one-shot and is responsible for loading a single image.
Not enough information here to tell, but probably you have released iconDownloader directly in some other part of the code we cannot see without setting the reference to nil at that time.
Then in viewDidUnload you are trying to release an invalid reference.
For #synthesize, use:
#synthesize iconDownLoader = _iconDownloader;
then fix all of the compiler warnings to use self.icondownloder instead of iconDownloader, and eliminate all uses of "release" (assuming your property is marked as retain which it should be).
In fact, perhaps your whole problem is that the property is not a retain property so you make the iconDOwnloader and it's promptly released.

GCD and KVO problems

My app is want to get the albums list of the iphone and all the photos in certain album.
In the app I enumerate the photos in one album of the iphone.
As there may be lots of photos of certain album, considering of performance I use GCD:dispatch_async. But it always crashes when the tableview cell updates which is invoked by KVO.
I've no idea about whether I use KVO or GCD in a wrong way.
Now alternatively I use performSelectorInBackground: replacing of dispatch_async. Now the app is not crashed but the app's performance is poor: the title of the cell will only be shown when you touch on it or scroll the tableview when there are many photos. In other words, the main thread must be blocked.
Attached is the code and the core code is in AlbumListViewController.m.
Can any one help me to check it ?
I just want to know:
1 why the app is crashed if using dispatch_async
2 how can I improve the performance in case of many photos.
thanks.
Below is my Code:
//
// RootViewController.h
// AlbumDemo
#import
#interface RootViewController : UITableViewController {
NSMutableArray *_listArray;
}
#property (nonatomic, retain) NSMutableArray *listArray;
#end
// RootViewController.m
#import "RootViewController.h"
#import
#import "AlbumListViewController.h"
NSString *thumnail = #"thumnail";
NSString *albumName = #"albumName";
NSString *albumNum = #"albumNum";
NSString *albumGroup = #"albumGroup";
#implementation RootViewController
#synthesize listArray = _listArray;
#pragma -
#pragma Function
- (void)setUp
{
_listArray = [[NSMutableArray alloc] initWithCapacity:1];
self.title = #"Albums";
}
- (void)fetchAlbumList
{
ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease];
ALAssetsFilter *fileter = [ALAssetsFilter allPhotos];
[assetLib enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
if (group)
{
[group setAssetsFilter:fileter];
NSString *_groupName = [group valueForProperty:ALAssetsGroupPropertyName];
NSNumber *_groupNum = [NSNumber numberWithInteger:[group numberOfAssets]];
UIImage *_groupImage = [UIImage imageWithCGImage:[group posterImage]];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:_groupName,albumName,_groupNum,albumNum,_groupImage,thumnail,group,albumGroup, nil];
[_listArray addObject:dic];
[self.tableView reloadData];
}
else
{
NSLog(#"_listArray :%#",_listArray);
}
}
failureBlock:^(NSError *error)
{
NSLog(#"Error: %#", error);;
}
];
}
#pragma -
#pragma ViewController lift cycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self setUp];
[self fetchAlbumList];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_listArray count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UILabel *nameLab = nil;
UILabel *numLab = nil;
UIImageView *thumnailImage = nil;
UIFont *font = [UIFont boldSystemFontOfSize:18];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
thumnailImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0,50, 50)];
thumnailImage.tag = 100;
[cell.contentView addSubview:thumnailImage];
[thumnailImage release];
nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 100, 30)];
nameLab.tag = 200;
nameLab.backgroundColor = [UIColor clearColor];
nameLab.font = font;
[cell.contentView addSubview:nameLab];
[nameLab release];
numLab = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 50, 30)];
numLab.tag = 300;
numLab.backgroundColor = [UIColor clearColor];
numLab.textColor = [UIColor grayColor];
numLab.font = font;
[cell.contentView addSubview:numLab];
[numLab release];
}
else
{
thumnailImage = (UIImageView *)[cell.contentView viewWithTag:100];
nameLab = (UILabel *)[cell.contentView viewWithTag:200];
numLab = (UILabel *)[cell.contentView viewWithTag:300];
}
NSDictionary *dic = [self.listArray objectAtIndex:indexPath.row];
thumnailImage.image = (UIImage *)[dic valueForKey:thumnail];
NSString *title = [dic valueForKey:albumName];
CGSize titleSize = [title sizeWithFont:font];
CGRect rect = nameLab.frame;
rect.size = titleSize;
nameLab.frame = rect;
nameLab.text = title;
rect = numLab.frame;
rect.origin.x = 60 + nameLab.frame.size.width + 10;
numLab.frame = rect;
numLab.text = [NSString stringWithFormat:#"(%d)",[[dic valueForKey:albumNum] intValue]];
// Configure the cell.
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dic = [self.listArray objectAtIndex:indexPath.row];
AlbumListViewController *viewController = [[AlbumListViewController alloc] initWithAssetGroup:[dic valueForKey:albumGroup]];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc
{
My_Release (_listArray);
[super dealloc];
}
#end
// AlbumListViewController.h
// AlbumDemo
#import
#import
#interface AlbumListViewController : UITableViewController {
NSMutableArray *_marr;
ALAssetsGroup *_assetsGroup;
}
#property (nonatomic, retain) NSMutableArray *list;
#property (nonatomic, retain) ALAssetsGroup *assetsGroup;
- (id)initWithAssetGroup:(ALAssetsGroup *)group;
#end
// AlbumListViewController.m
// AlbumDemo
#import "AlbumListViewController.h"
#interface PhotoObj : NSObject {
NSString *_name;
UIImage *_thumbnail;
UIImage *_fullImage;
}
#property (nonatomic, copy ) NSString *name;
#property (nonatomic, retain) UIImage *thumbnail;
#property (nonatomic, retain) UIImage *fullImage;
#end
#implementation PhotoObj
#synthesize name = _name;
#synthesize thumbnail = _thumbnail,fullImage = _fullImage;
- (void)dealloc
{
My_Release(_thumbnail);
My_Release(_fullImage);
My_Release(_name);
[super dealloc];
}
#end
#interface AlbumListViewController()
- (NSMutableArray*)list;
- (NSUInteger)countOfList;
- (id)objectInListAtIndex:(NSUInteger)idx;
- (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx;
- (id)objectInListAtIndex:(NSUInteger)idx;
- (void)removeObjectFromListAtIndex:(NSUInteger)idx;
- (void)replaceObjectInListAtIndex:(NSUInteger)idx withObject:(id)anObject;
- (void)setList:(NSMutableArray *)_arr;
#end
#implementation AlbumListViewController
#synthesize assetsGroup = _assetsGroup;
- (id)initWithAssetGroup:(ALAssetsGroup *)group
{
self = [self initWithStyle:UITableViewStylePlain];
if (self )
{
_marr = [[NSMutableArray alloc] initWithCapacity:1];
self.assetsGroup = group;
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
My_Release(_marr);
My_Release(_assetsGroup);
[self removeObserver:self forKeyPath:#"list"];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)parseAssetGroup
{
[_marr removeAllObjects];
[self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result)
{
PhotoObj *obj = [[PhotoObj alloc] init];
obj.thumbnail = [UIImage imageWithCGImage:[result thumbnail]];
ALAssetRepresentation *represention = [result defaultRepresentation];
obj.fullImage = [UIImage imageWithCGImage:[represention fullScreenImage]];
obj.name = [[represention url] absoluteString];
[self willChangeValueForKey:#"list"];
[self insertObject:obj inListAtIndex:[_marr count]];
[self didChangeValueForKey:#"list"];
My_Release(obj);
}
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self addObserver:self forKeyPath:#"list" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:NULL];
/*
if performSelectorInBackground, the perofrmance is poor
as the title of the cell will be shown in a long time and it now seems the main thread is blocked
*/
[self performSelectorInBackground:#selector(parseAssetGroup) withObject:nil];
/*
using dispatch_async it always crashes
as it says the sth is wrong with the tableview update
*/
// dispatch_async(dispatch_get_main_queue(), ^{
// [self parseAssetGroup];
// });
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#pragma mark - Table view data source
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [_marr count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UIImageView *thumbNail = nil;
UILabel *nameLab = nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
thumbNail = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
thumbNail.tag = 99;
[cell.contentView addSubview:thumbNail];
[thumbNail release];
nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 240, 40)];
nameLab.numberOfLines = 2;
nameLab.font = [UIFont systemFontOfSize:16];
nameLab.tag = 199;
[cell.contentView addSubview:nameLab];
[nameLab release];
}
else
{
thumbNail = (UIImageView *)[cell.contentView viewWithTag:99];
nameLab = (UILabel *)[cell.contentView viewWithTag:199];
}
// Configure the cell...
PhotoObj *obj = [_marr objectAtIndex:indexPath.row];
nameLab.text = obj.name;
thumbNail.image = obj.thumbnail;
return cell;
}
#pragma mark -
- (NSUInteger)countOfList
{
return [_marr count];
}
- (NSMutableArray*)list
{
return _marr;
}
- (void)setList:(NSMutableArray *)_arr
{
if (_marr != _arr)
{
[_marr release];
_marr = _arr;
}
}
- (id)objectInListAtIndex:(NSUInteger)idx
{
return [_marr objectAtIndex:idx];
}
- (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx
{
if ([NSThread isMainThread])
{
NSLog(#"insert main thread");
}
else
{
NSLog(#"insert not main thread");
}
[_marr insertObject:anObject atIndex:idx];
}
- (void)removeObjectFromListAtIndex:(NSUInteger)idx
{
[_marr removeObjectAtIndex:idx];
}
- (void)replaceObjectInListAtIndex:(NSUInteger)idx withObject:(id)anObject
{
[_marr replaceObjectAtIndex:idx withObject:anObject];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSIndexSet *indices = [change objectForKey:NSKeyValueChangeIndexesKey];
if (indices == nil)
return; // Nothing to do
// Build index paths from index sets
NSUInteger indexCount = [indices count];
NSUInteger buffer[indexCount];
[indices getIndexes:buffer maxCount:indexCount inIndexRange:nil];
NSMutableArray *indexPathArray = [NSMutableArray array];
for (int i = 0; i
I ran into exactly the same problem today. In short, the reason is you cannot do UIKit related tasks, like updating a table, or in my case a Textview, from the background dispatch queue. Check the link below for more details.
comparison GCD vs. performSelectorInBackground: dispatch_async not in background
A possible solution is the following: instead of assigning your fresh data in your update block directly to the KVO variable which causes the crash, you dispatch another block which does this to the main queue, from inside your update block. If you use the dispatch_async_f function to do this, you can pass a pointer to your data as context.
Like this:
dispatch_async(yourQueue, ^() {
NSArray *data;
// do stuff to alloc and fill the array
// ...
dispatch_async(dispatch_get_main_queue(), ^() {
myObj.data = data; // the assignment, which triggers the KVO.
});
});
For me this works without retaining and releasing the data. Not sure, if this correct.

Resources