uitableview visible cells not scrolling - ios

I have a tableView that holds, in each row, the output of a running timer. It works fine until I try to add a cell beyond the end of the view, and then the last cell displayed gets overlaid with the new timer. I am using reusable cells and I doubt very much that there are memory problems. If someone has a suggestion, I would appreciate it. I'm not posting the whole code because it's pretty messy (I'm a new developer) but here are the code sections that have to do with the cells.
- (void)tableView: (UITableView*)tableView
willDisplayCell: (UITableViewCell*)cell
forRowAtIndexPath: (NSIndexPath*)indexPath
{
cell.backgroundColor = indexPath.row % 2
? [UIColor colorWithRed: 0.0 green: 0.3 blue: 1.0 alpha: 0.3]
: [UIColor whiteColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
}
// *********************************************************************
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[NRCItemStore sharedStore] allItems] count];
}
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 60.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get a new or recycled cell
NRCItemCell *cell = [tableView dequeueReusableCellWithIdentifier:#"NRCItemCell" forIndexPath:indexPath];
self.timerItem.cell = cell;
[self handleTimer];
return cell;
//
// NRCItemCell.h
// timeLogger
//
// Created by Nelson Capes on 10/8/15.
// Copyright © 2015 Nelson Capes. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface NRCItemCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *timerName;
#property (weak, nonatomic) IBOutlet UILabel *timer;
#property (weak, nonatomic) IBOutlet UIImageView *thumbnailView;
#end
//
// NRCItemStore.m
// timeLogger
#import "NRCItemStore.h"
#import "NRCtimerItem.h"
#import "NRCImageStore.h"
#interface NRCItemStore ()
#property (nonatomic) NSMutableArray *privateItems;
#end
#implementation NRCItemStore
+ (instancetype)sharedStore
{
static NRCItemStore *sharedStore;
// Do I need to create a sharedStore?
if (!sharedStore) {
sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}
// If a programmer calls [[NRCItemStore alloc] init], let him
// know the error of his ways
- (instancetype)init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"Use +[NRCItemStore sharedStore]"
userInfo:nil];
return nil;
}
// Here is the real (secret) initializer
- (instancetype)initPrivate
{
self = [super init];
if (self) {
NSString *path = [self itemArchivePath];
_privateItems = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// If the array hadn't been saved previously, create a new empty one
if (!_privateItems) {
_privateItems = [[NSMutableArray alloc] init];
}
}
return self;
}
- (NSString *)itemArchivePath
{
// Make sure that the first argument is NSDocumentDirectory
// and not NSDocumentationDirectory
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// Get the one document directory from that list
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:#"items.archive"];
}
- (BOOL)saveChanges
{
NSString *path = [self itemArchivePath];
// Returns YES on success
return [NSKeyedArchiver archiveRootObject:self.privateItems toFile:path];
}
- (NSArray *)allItems
{
return [self.privateItems copy];
}
- (NRCtimerItem *)createItem
{
NRCtimerItem *item = [[NRCtimerItem alloc] init];
[self.privateItems addObject:item];
return item;
}
- (void)removeItem:(NRCtimerItem *)item
{
[self.privateItems removeObjectIdenticalTo:item];
}
- (void)moveItemAtIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex
{
if (fromIndex == toIndex) {
return;
}
// Get pointer to object being moved so you can re-insert it
NRCtimerItem *item = self.privateItems[fromIndex];
// Remove item from array
[self.privateItems removeObjectAtIndex:fromIndex];
// Insert item in array at new location
[self.privateItems insertObject:item atIndex:toIndex];
}
#end

Related

How to save the checkmarks in uitableview and show them when the user comes back again to the view?

I am using the following code for showing the checkmarks in the uitableview
{
// NSArray *tableContents;
NSMutableArray *selectedMarks; // You need probably to save the selected cells for use in the future.
}
#property (strong, nonatomic) IBOutlet UITableView *languageTableView;
#property (nonatomic, strong) NSArray *tableContents;
#end
#implementation QPLanguageSettingsController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initialisation];
selectedMarks = [NSMutableArray new];
}
#pragma mark - View Life Cycle
-(void)initialisation
{
_tableContents = [NSArray arrayWithObjects:#"English",#"Spanish",#"Russian",#"Arabic",#"Portuguese",#"French",#"German",#"German",#"German",#"German",#"German",#"German",#"German",#"German", nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - UITableView delegate & datasources
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 14;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//etc.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.textColor = [UIColor whiteColor];
cell.backgroundColor = [UIColor clearColor];
[cell setIndentationLevel:3];
[cell setIndentationWidth:10];
NSString *text = [_tableContents objectAtIndex:[indexPath row]];
//cell.isSelected = [selectedMarks containsObject:text] ? YES : NO;
cell.textLabel.text = text;
NSDictionary *item = [_tableContents objectAtIndex:indexPath.row];
if ([selectedMarks containsObject:item])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//if you want only one cell to be selected use a local NSIndexPath property instead of array. and use the code below
//self.selectedIndexPath = indexPath;
//the below code will allow multiple selection
NSDictionary *item = [_tableContents objectAtIndex:indexPath.row];
if ([selectedMarks containsObject:item])
{
[selectedMarks removeObject:item];
}
else
{
[selectedMarks addObject:item];
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
But the problem is that when i come again to the view controller all the checkmarks disappears. how to solve it. Remember i am using multiple selection in uitableview.
I tried the to get solution for your question.I got it successfully.It works perfectly.
This is sample one.Try this code.It works fine.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableViewCheckMarkPreviousSelectionUpdate;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray *arrProductSelection,*arrProductSelectDeSelectCheckMark;
NSArray *arrayFetchFromDefaults;
}
#end
#implementation ViewController
#synthesize tableViewCheckMarkPreviousSelectionUpdate;
- (void)viewDidLoad
{
[super viewDidLoad];
arrProductSelection = [[NSMutableArray alloc]initWithObjects:#"iPhone",#"iPad",#"iPod",#"iTV",#"iWatch",#"iMac",nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewWillAppear:(BOOL)animated
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
arrayFetchFromDefaults = [userDefaults objectForKey:#"selectedcheckmark"];
arrProductSelectDeSelectCheckMark = [[NSMutableArray alloc]initWithArray:arrayFetchFromDefaults];
if(arrProductSelectDeSelectCheckMark.count == 0)
{
arrProductSelectDeSelectCheckMark = [[NSMutableArray alloc]init];
for(int j=0;j<[arrProductSelection count];j++)
{
[arrProductSelectDeSelectCheckMark addObject:#"deselected"];
}
}
[tableViewCheckMarkPreviousSelectionUpdate reloadData];
}
#pragma mark - UITableViewDataSource Methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return arrProductSelection.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *strCell = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:strCell];
if(cell==nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCell];
}
if([[arrProductSelectDeSelectCheckMark objectAtIndex:indexPath.row] isEqualToString:#"deselected"])
cell.accessoryType = UITableViewCellAccessoryNone;
else
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.textLabel.text = [arrProductSelection objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - UITableViewDelegate Methods
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
#try
{
CGPoint touchPoint = [cell convertPoint:CGPointZero toView:tableViewCheckMarkSelectionUpdate];
NSIndexPath *indexPath = [tableViewCheckMarkSelectionUpdate indexPathForRowAtPoint:touchPoint];
NSLog(#"%#",arrProductSelectDeSelectCheckMark);
if([arrProductSelectDeSelectCheckMark count]==0)
{
for(int i=0; i<[arrProductSelection count]; i++)
{
[arrProductSelectDeSelectCheckMark addObject:#"deselected"];
}
}
if([[arrProductSelectDeSelectCheckMark objectAtIndex:indexPath.row] isEqualToString:#"deselected"])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[arrProductSelectDeSelectCheckMark replaceObjectAtIndex:indexPath.row withObject:#"selected"];
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
[arrProductSelectDeSelectCheckMark replaceObjectAtIndex:indexPath.row withObject:#"deselected"];
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arrProductSelectDeSelectCheckMark forKey:#"selectedcheckmark"];
[defaults synchronize];
}
#catch (NSException *exception) {
NSLog(#"The exception is-%#",exception);
}
}
#end
You have multiple solutions:
Using a rational DB on the device like Core-data or SQLite which actually fits your use case but number 3 is more suitable for your use case.
Using NSUserDefaults:
At runtime, you use an NSUserDefaults object to read the defaults that your application uses from a user’s defaults database. NSUserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default
You can find an example here. But NSUserDefaults is to save settings/config/setup information or perhaps user info and NOT for the use case you need.
Using NSCoder which is in my opinion fits your use case.
you can create a Singleton and store data on it :
Singleton.h
#interface Singleton : NSObject
#property (nonatomic, strong) NSArray *rowsChecked;
+ (Singleton *)sharedInstance;
+ (NSArray *)getRowsChecked;
+ (void)setRowsChecked:(NSArray *)rowsChecked;
#end
Singleton.m
#import "Singleton.h"
#implementation Singleton
static Singleton *sharedObject;
+ (Singleton *)sharedInstance;
{
if (sharedObject == nil) {
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedObject = [[Singleton alloc] init];
});
}
return sharedObject;
}
+ (NSArray *)getRowsChecked
{
Singleton *singleton = [Singleton sharedInstance];
return singleton.rowsChecked;
}
+ (void)setRowsChecked:(NSArray *)rowsChecked
{
Singleton *singleton = [Singleton sharedInstance];
singleton.rowsChecked= rowsChecked;
}
#end
and to access to your Singleton :
[[Singleton sharedInstance] getRowsChecked]
// or
[[Singleton sharedInstance] setRowsChecked:anArray];
Use the following code:
#import "ViewController.h"
#interface ViewController ()<UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *languagesTableView;
#property (strong, nonatomic) NSArray *languagesArray;
#property (strong, nonatomic) NSMutableArray *checkMarksArray;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.languagesTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"cell"];
self.languagesArray = [NSArray arrayWithObjects:#"English",#"Spanish",#"Russian",#"Arabic",#"Portuguese",#"French",#"German", nil];
self.checkMarksArray = [[NSMutableArray alloc]init];
if( [[NSUserDefaults standardUserDefaults]objectForKey:#"selectedRowsArray"])
{
self.checkMarksArray = [[[NSUserDefaults standardUserDefaults]objectForKey:#"selectedRowsArray"] mutableCopy];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.languagesArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *languagesCell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
languagesCell.textLabel.text = self.languagesArray[indexPath.row];
if([self.checkMarksArray containsObject:[NSNumber numberWithLong:indexPath.row]])
{
languagesCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
languagesCell.accessoryType = UITableViewCellAccessoryNone;
}
[[NSUserDefaults standardUserDefaults]setObject:self.checkMarksArray forKey:#"selectedRowsArray"];
[[NSUserDefaults standardUserDefaults]synchronize];
return languagesCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if([self.checkMarksArray containsObject:[NSNumber numberWithLong:indexPath.row]])
{
[self.checkMarksArray removeObject:[NSNumber numberWithLong:indexPath.row]];
}
else
{
[self.checkMarksArray addObject:[NSNumber numberWithLong:indexPath.row]];
}
[self.languagesTableView reloadData];
}
#end
Check this GitHub link:
https://github.com/k-sathireddy/TableViewSelectedCheckMarks

UISlider Value Automatically assigned while scrolling in UITable View Cell

I am Working on a Program which looks like this:
There is a Table View i that there is a Custom Table View Cell.
And there is a UISlider , Label and Button on Custom View Cell
Now the Problem is when i slide the UISlider of Cell : 0 than the UISlider at Cell : 12(or later Cell) is Automatically assigned the Cell:0's UISlider Value(Thanks To ARC..!!).
Now anyone have a solution so that the later cell's UISlider doest change while i change value of upper Cells.
P.S. When i assigned a UiSlider Value at Cell:0 and Scroll Up and Down it is automatically random Cell's UISlider Value is changing.
Google Drive Link of Project :
Slider Program
I am Using xCode 5 and iOS SDK 7.
Thanks for Reading.
Edit:
cellForRowAtIndexPath Method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableCell = #"Cell";
CustomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableCell];
if (cell == nil) {
cell = [[CustomeTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableCell];
}
NSString *strName = [NSString stringWithFormat:#"Cell : %d",indexPath.row];
// NSLog(#"strName :%# , SliderValue : %d",strName , (int)cell.mySlider.value);
for (int i = 0; i < arrSlider.count; i++) {
NSString *strTag = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]valueForKey:#"tag"]];
NSString *myIndexPath = [NSString stringWithFormat:#"%d",indexPath.row];
if([strTag isEqualToString:myIndexPath])
{
NSString *strValue = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]valueForKey:#"value"]];
cell.mySlider.value = [strValue floatValue];
NSLog(#"Tag Value : %# , value %f", strTag , [strValue floatValue]);
}
}
[cell.btnCell setTitle:strName forState:UIControlStateNormal];
cell.btnCell.tag = indexPath.row;
cell.mySlider.tag = indexPath.row;
[cell.mySlider addTarget:self action:#selector(customSliderValue:) forControlEvents:UIControlEventValueChanged];
[cell.btnCell addTarget:self action:#selector(customeBtnClicked:) forControlEvents:UIControlEventTouchDown];
return cell;
}
Use NSMutableDictionary to hold the values of slider then update it from the cellForRowAtIndexPath method i am posting the changes just make changes in your project
in ViewCOntroller.h file
#import <UIKit/UIKit.h>
#import "CustomeTableViewCell.h"
#interface ViewController : UIViewController <UITableViewDataSource ,UITableViewDelegate,SliderDelegate>//confirms to delegate
{
//NSArray *tableList;
UITableView *mytableview;
NSInteger SliderChangeValue;
}
#property (strong , nonatomic) IBOutlet UIView *tableDemo;
#property (strong , nonatomic) NSMutableArray *arrSlider;
#property (strong, nonatomic) NSMutableDictionary *sliderDicValues; //add a mutable dictionary
#property (weak, nonatomic) IBOutlet UITableView *myTableView;//add outlet to tableview
#end
in ViewController.mfile
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize arrSlider;
#synthesize sliderDicValues;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// tableList = [NSArray arrayWithObjects:
// #"Cell 1",#"Cell 2",#"Cell 3",#"Cell 4",#"Cell 5",
// #"Cell 6",#"Cell 7",#"Cell 8",#"Cell 9",#"Cell 10",
// #"Cell 11",#"Cell 12",#"Cell 13",#"Cell 14",#"Cell 15",
// #"Cell 16",#"Cell 17",#"Cell 18",#"Cell 19",#"Cell 20",
// nil];
arrSlider = [[NSMutableArray alloc]init];
sliderDicValues = [[NSMutableDictionary alloc]init]; //initilise an mutable dictionary
//[mytableview registerClass:[CustomeTableViewCell class] forCellReuseIdentifier:#"Cell"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
//[tableList count]
return 15;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableCell = #"Cell";
CustomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableCell];
if (cell == nil) {
cell = [[CustomeTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableCell];
}
if([self.sliderDicValues objectForKey:[NSString stringWithFormat:#"%d",indexPath.row]]) //check if there is any slided value is present
{
NSNumber *value = [self.sliderDicValues objectForKey:[NSString stringWithFormat:#"%d",indexPath.row]];
[cell.mySlider setValue:value.integerValue]; //set the slider value
[cell.myCellLabel setText:[NSString stringWithFormat:#"%d",value.integerValue]];//and also label
}
else //set to default values
{
[cell.mySlider setValue:(NSInteger)0];
[cell.myCellLabel setText:#"label"];
}
//add a single target don't add double target to slider
cell.sliderDelegate = self;//set the delegate
return cell;
/*
NSString *strName = [NSString stringWithFormat:#"Cell : %d",indexPath.row];
NSLog(#"strName :%# , SliderValue : %d",strName , (int)cell.mySlider.value);
for (int i = 0; i < arrSlider.count; i++) {
NSString *strTag = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]valueForKey:#"tag"]];
NSString *myIndexPath = [NSString stringWithFormat:#"%d",indexPath.row];
if([strTag isEqualToString:myIndexPath])
{
NSString *strValue = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]valueForKey:#"value"]];
cell.mySlider.value = [strValue floatValue];
NSLog(#"Tag Value : %# , value %f", strTag , [strValue floatValue]);
}
if ([strTag isEqual:myIndexPath]) {
//NSString *strValue = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]objectForKey:#"value"]];
//NSLog(#"%#",strValue);
NSLog(#"Hello");
//cell.mySlider.value =
}
}
[cell.btnCell setTitle:strName forState:UIControlStateNormal];
cell.btnCell.tag = indexPath.row;
cell.mySlider.tag = indexPath.row;
[cell.mySlider addTarget:self action:#selector(customSliderValue:) forControlEvents:UIControlEventValueChanged];
[cell.btnCell addTarget:self action:#selector(customeBtnClicked:) forControlEvents:UIControlEventTouchDown];
*/
}
//delegate method called from custom cell
- (void)sliderChanged:(CustomeTableViewCell *)cell
{
NSIndexPath *path = [_myTableView indexPathForCell:cell]; //get the indexpath
if(path)//check if valid path
{
SliderChangeValue = cell.mySlider.value;
[self.sliderDicValues setObject:[NSNumber numberWithInt:SliderChangeValue] forKey:[NSString stringWithFormat:#"%d",path.row]]; //set the value in the dictionary later used in the cellForRowAtIndexPath method
}
// SliderChangeValue = (int)sender.value;
NSLog(#"%d",SliderChangeValue);
}
//dont use it
-(void)customSliderValue:(UISlider *)sender{
// NSString *value =[NSString stringWithFormat:#"%d" ,(int)sender.value];
// NSString *tag = [NSString stringWithFormat:#"%d", (int)sender.tag];
//
// NSLog(#"%# %#",value , tag);
//
// [self.dicSilder setObject:value forKey:#"value"];
// [self.dicSilder setObject:tag forKey:#"tag"];
//
// [self.arrSlider addObject:self.dicSilder];
// NSLog(#"%#",self.arrSlider);
SliderChangeValue = (int)sender.value;
NSLog(#"%d",SliderChangeValue);
}
//this is also put a delegate from the cell like slider , just add the another method in the protocol and perform action, if u don't get just comment i will update the code and u hav t modify this method according to your requirement
-(void)customeBtnClicked:(UIButton *)sender
{
NSString *value =[NSString stringWithFormat:#"%d" ,SliderChangeValue];
NSString *tag = [NSString stringWithFormat:#"%d", sender.tag];
//NSLog(#"%# %#",value,tag);
NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
[dic setObject:value forKey:#"value"];
[dic setObject:tag forKey:#"tag"];
//NSLog(#"%#",dic);
[arrSlider addObject:dic];
NSLog(#"%#",arrSlider);
NSString *sliderTagAtIndexPath = #"";
//NSString *sliderValueAtindexPath = #"";
for (int i = 0; i < arrSlider.count; i++) {
NSString *strTag = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]valueForKey:#"tag"]];
if([strTag isEqualToString:tag])
{
//NSString *strValue = [NSString stringWithFormat:#"%#",[[arrSlider objectAtIndex:i]valueForKey:#"value"]];
sliderTagAtIndexPath = strTag;
//sliderValueAtindexPath = strValue;
}
}
UIAlertView *myAlertView = [[UIAlertView alloc]initWithTitle:#"Clicked"
message:[NSString stringWithFormat:#"Cell : %# Value: %d", sliderTagAtIndexPath ,SliderChangeValue]
//message:[NSString stringWithFormat:#"Cell : %# Value: %#", sliderTagAtIndexPath ,sliderValueAtindexPath]
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[myAlertView show];
}
#end
in CustomeTableViewCell.h file
#import <UIKit/UIKit.h>
//add a custom delegate
#protocol SliderDelegate<NSObject>
- (void)sliderChanged:(id)self;
#end
#interface CustomeTableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *myCellLabel;
#property (weak, nonatomic) IBOutlet UISlider *mySlider;
#property (weak, nonatomic) IBOutlet UIButton *btnCell;
#property (weak, nonatomic) id <SliderDelegate>sliderDelegate;
- (IBAction)sliderValuechanged:(UISlider *)sender;
#end
in CustomeTableViewCell.m file
#import "CustomeTableViewCell.h"
#implementation CustomeTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)awakeFromNib
{
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (IBAction)sliderValuechanged:(UISlider *)sender
{
self.myCellLabel.text = [NSString stringWithFormat:#"%d",(NSInteger)sender.value];
//call the custom delegate each time when slider is slided
if([_sliderDelegate respondsToSelector:#selector(sliderChanged:)])
{
[_sliderDelegate sliderChanged:self]; //passing the entire cell itself
}
}
#end
Hope this helps u .. :)
You don't need to check(set) all the datasource for a Cell , I mean, No need of for loop inside the cellForRowAtIndexPath. just remove it and will work fine .
Try this code it works for me :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"%d",indexPath.row];
CustomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[CustomeTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
// Write your rest code here
return cell;
}

sudden EXC_BAD_ACCESS at - (NSString *)tableView: titleForHeaderInSection:

I think just showing my whole code is better than my explanation.. actually I'm not good at expressing something in English.X(
this code is .h file for .m file below.
#import <UIKit/UIKit.h>
#define kImageValueTag 0
#define kNameValueTag 1
#define kSubtitleValueTag 2
#define kMemoValueTag 3
#class PhonebookDetailedViewController;
#interface PhonebookViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
UITableViewCell *tvCell;
UITableView *tableView;
UISearchBar *searchBar;
NSString *fullName;
NSMutableDictionary *names, *pictures;
NSArray *keys, *sortedKeys, *sortedAllValues;
PhonebookDetailedViewController *childController;
}
#property (nonatomic, retain) IBOutlet UITableViewCell *tvCell;
#property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) UIImage *phoneImage;
#property (nonatomic, retain) NSString *fullName;
#property (nonatomic, retain) NSMutableDictionary *names;
#property (nonatomic, retain) NSMutableDictionary *pictures;
#property (nonatomic, retain) NSArray *keys;
#property (nonatomic, retain) NSArray *sortedKeys;
#property (nonatomic, retain) NSArray *sortedAllValues;
#end
this code is for the .m file that implement showing a table. When I try to scroll the table down, the error named EXC_BAD_ACCESS is suddenly called at - (NSString *)tableView: titleForHeaderInSection: method. I guess sortedKeys releases at somewhere because I can do [sortedKeys count] before error, but cannot when error comes. I don't know where it releases and why it releases, however. Please show your ideas to me. Any ideas are okay. Thank you in advance.
#import "PhonebookViewController.h"
#import "PhonebookDetailedViewController.h"
#import "NSString-SortForIndex.h"
#implementation PhonebookViewController
#synthesize tvCell;
#synthesize searchBar;
#synthesize tableView;
#synthesize phoneImage;
#synthesize fullName;
#synthesize names, pictures;
#synthesize keys, sortedAllValues, sortedKeys;
//- (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
{
[super viewDidLoad];
CGRect bounds = self.tableView.bounds;
bounds.origin.y = bounds.origin.y + searchBar.bounds.size.height;
self.tableView.bounds = bounds;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *nameFilePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"nameContacts.dict"];
NSString *pictureFilePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"imageContacts.dict"];
self.names = (NSMutableDictionary *)[NSKeyedUnarchiver unarchiveObjectWithFile:nameFilePath];
self.pictures = (NSMutableDictionary *)[NSKeyedUnarchiver unarchiveObjectWithFile:pictureFilePath];
self.keys = [self.names allKeys];
sortedKeys = [self.keys sortedArrayUsingSelector:#selector(sortForIndex:)];
sortedAllValues = [[NSArray alloc] init];
for (NSString *sortedKey in sortedKeys)
{
NSArray *selectedValues = [self.names valueForKey:sortedKey];
for (NSString *selectedValue in selectedValues)
sortedAllValues = [sortedAllValues arrayByAddingObject:selectedValue];
}
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.tableView = nil;
self.searchBar = nil;
self.tvCell = nil;
self.fullName = nil;
self.names = nil;
self.pictures = nil;
self.keys = nil;
self.sortedKeys = nil;
self.sortedAllValues = nil;
}
- (void)dealloc
{
[tableView release];
[searchBar release];
[tvCell release];
[fullName release];
[names release];
[pictures release];
[keys release];
[sortedKeys release];
[sortedAllValues release];
[super dealloc];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sortedKeys count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *key = [sortedKeys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
return [nameSection count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
if ( cell == nil )
{
NSArray *nib = [[NSBundle mainBundle]
loadNibNamed:#"CustomPhonebookCell"
owner:self
options:nil];
if (nib.count > 0)
{
cell = self.tvCell;
} else
{
NSLog(#"Failed to load CustomPhonebookCell nib file!");
}
}
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
NSUInteger row = [indexPath row];
NSUInteger section = [indexPath section];
NSString *foundKey = [sortedKeys objectAtIndex:section];
NSArray *nameSection = [self.names objectForKey:foundKey];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:kNameValueTag];
nameLabel.text = [nameSection objectAtIndex:row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *key = [sortedKeys objectAtIndex:section]; // EXC_BAD_ACCESS error at here
return key;
}
sortedKeys is not properly retained. When you set it use self.
self.sortedKeys = [self.keys sortedArrayUsingSelector:#selector(sortForIndex:)];

EXC_BAD_ACCESS crash when switching back and forth between views

I'm getting a EXC_BAD_ACCESS crash when switching back and forth between views. I'm having a problem finding the cause of this crash. In the simulator it always goes back to the main.m file and reports the crash in it.
But on my device the EXC_BAD_ACCESS show up on my custom UITableViewCell when I release it in the dealloc method. If I enable NSZombieEnabled my app doesn't crash at all.
Here is the .h file
#import <UIKit/UIKit.h>
#define kWinsAmountTagValue 2 // how many wins you have
#define kWinningsAmountTagValue 3 // how much money you won
#interface MyStatsViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource,
UINavigationBarDelegate, UINavigationControllerDelegate>{
NSArray *list;
UITableView *theTable;
UITableViewCell *theCell;
}
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, retain) IBOutlet UITableView *theTable;
#property (nonatomic, retain) IBOutlet UITableViewCell *theCell;
// dealloc and cleanup
-(void) dealloc;
// misc methods
-(void)loadData;
// demo data
-(NSArray *)tableData;
#end
Here is my .m file
#import "MyStatsViewController.h"
#implementation MyStatsViewController
#synthesize list;
#synthesize theTable;
#synthesize theCell;
#pragma mark - dealloc and cleanup
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
NSLog(#"Memory Warning");
// 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;
self.list = nil;
self.theTable = nil;
self.theCell = nil;
}
- (void)dealloc
{
[super dealloc];
[list release];
[theTable release];
[theCell release];
}
#pragma mark - misc methods
-(void) loadData
{
self.list = [self tableData];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(void)viewWillAppear:(BOOL)animated
{
[self loadData];
[theTable reloadData];
}
#pragma mark - Table Data Source Methods
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [list count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier =#"MyStatsCustomCellIdentifer";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
NSUInteger row = [indexPath row];
if (cell == nil) {
if (row == [list count] -1) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
} else {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyStatsCustomCell"
owner:self
options:nil];
if ([nib count] > 0) {
cell = self.theCell;
} else {
NSLog(#"failed to load MyStatsCustomCell");
}
}
}
// Add custom stuff here for rows
//cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (row == [list count] -1) {
cell.textLabel.text = [list objectAtIndex:row];
} else {
UILabel *prizeLevel = (UILabel *)[cell viewWithTag:kPrizeLevelTagValue];
prizeLevel.text = [[list objectAtIndex:row] objectForKey:#"prizeLevel"];
UILabel *winsAmount = (UILabel *)[cell viewWithTag:kWinsAmountTagValue];
winsAmount.text = [[list objectAtIndex:row] objectForKey:#"winsAmount"];
UILabel *winningsAmount = (UILabel *)[cell viewWithTag:kWinningsAmountTagValue];
winningsAmount.text = [[list objectAtIndex:row] objectForKey:#"winningsAmount"];
}
//NSLog(#"theCell Retain: %i",[theCell retainCount]);
return cell;
}
#pragma mark - Table View Delegate Methods
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - demo data
-(NSArray *)tableData
{
NSArray *prizeLevels = [[NSArray alloc] initWithObjects:
#"6-of-6", #"5-of-6", #"4-of-6",#"3-of-6", nil];
NSArray *winsAmount = [[NSArray alloc] initWithObjects:
#"0", #"0", #"2", #"100", nil];
NSArray *winngingsAmount = [[NSArray alloc] initWithObjects:
#"$0",#"$0", #"$45.50",#"$125.00", nil];
NSMutableArray *myGames = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [prizeLevels count]; i++) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[prizeLevels objectAtIndex:i] forKey:#"prizeLevel"];
[dict setObject:[winsAmount objectAtIndex:i] forKey:#"winsAmount"];
[dict setObject:[winngingsAmount objectAtIndex:i] forKey:#"winningsAmount"];
[myGames addObject:dict];
[dict release];
}
[prizeLevels release];
[winsAmount release];
[winngingsAmount release];
[myGames addObject:#"Spent: $1250.00"];
return myGames;
}
#end
Any help would be appreciated.
It is a good practice to clean up class's own variables before calling the super's destructor. A lot more details can be found here: Why do I have to call super -dealloc last, and not first?.

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