I'm developing an app in Xcode in Objective-C.
The app has a TableView with an array of restaurants. My problem is that when I try to use the SearchBar I use the Title (name of the restaurant as a filter) but when I show the rows after the filter search, only the Title is right. The other label and the image in the cell is wrong (it shows me the correct title but the image and the other label (description label) is the same as the first row in the original tableview).
I have to change my cellForRowAtIndexPath method but I don't now how to change it.
This is TableViewController called MainTableViewController.h
#import <UIKit/UIKit.h>
#interface MainTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (weak, nonatomic) IBOutlet UIBarButtonItem *barButton;
//#property (nonatomic, strong) NSArray *Images;
//#property (nonatomic, strong) NSArray *Description;
//#property (nonatomic, strong) NSArray *Title;
#property (nonatomic, strong) NSMutableArray *Title;
#property (nonatomic, strong) NSMutableArray *Images;
#property (nonatomic, strong) NSMutableArray *Description;
#property (nonatomic, strong) NSMutableArray *filteredRest;
#property BOOL isFiltered;
#property (strong, nonatomic) IBOutlet UITableView *RestTableView;
#property (strong, nonatomic) IBOutlet UITableView *mySearchBar;
#end
This is my MainTableViewController.c
#import "MainTableViewController.h"
#import "SWRevealViewController.h"
#import "RestTableViewCell.h"
#import "RestViewController.h"
#interface MainTableViewController ()
#end
#implementation MainTableViewController
#synthesize mySearchBar, filteredRest, isFiltered;
- (void)viewDidLoad {
[super viewDidLoad];
_barButton.target = self.revealViewController;
_barButton.action = #selector(revealToggle:);
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
[self.navigationItem setTitle:#"MadEat"]; /*Cambia el titulo del navigation controller*/
[self.navigationController.navigationBar setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor whiteColor]}]; /*Cambia el color de las letras del navigation controller bar del menu principal*/
[self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:27/255.0f green:101/255.0f blue:163/255.0f alpha:1.0f]];
self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; /*Cambia el color del boton de la izquierda*/
self.RestTableView.tableFooterView = [[UIView alloc] init]; /*Esta linea hace que en la tabla solo aparezcan el numero de filas que tienes establecidas, es decir, que las vacias no aparezcan*/
/*Alerta que se muestra solo la primera vez. Está desactivada*/
if (![#"1" isEqualToString:[[NSUserDefaults standardUserDefaults] objectForKey:#"alert"]]) {
[[NSUserDefaults standardUserDefaults] setValue:#"1" forKey:#"alert"];
[[NSUserDefaults standardUserDefaults] synchronize];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Terms of use" message:#"The brands mentioned have no relationship with MadEat and the app has no any liability on that content." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"Accept" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:ok];
[self presentViewController:alertController animated:YES completion:nil];
}
_Title = #[#"80 Grados",
#"90 Grados",
#"B&B Babel",
#"Babelia",
#"Bacira",
#"Bar Galleta",
#"Bar Tomate",
#"Barra Atlantica",
#"BaRRa de Pintxos",
#"BaRRa de Pintxos",];
_Description = #[#"Barrio Malasaña",
#"Barrio Retiro",
#"Barrio Chueca",
#"Barrio de Salamanca",
#"Barrio Chamberí",
#"Barrio Malasaña",
#"Barrio Chamberí",
#"Barrio Malasaña",
#"Barrio del Pilar",
#"Barrio Retiro",];
_Images = #[#"80_grados.png",
#"90_grados",
#"babel.png",
#"babelia.png",
#"bacira.png",
#"bar_galleta.png",
#"bar_tomate.png",
#"barra_atlantica.png",
#"barra_de_pintxos.png",
#"barra_de_pintxos.png",];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//return _Title.count;
if (isFiltered == YES) {
return filteredRest.count;
} else {
return _Title.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TableCell";
RestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
if (isFiltered == YES) {
cell.TitleLabel.text = [filteredRest objectAtIndex:indexPath.row];
} else {
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _Description[row];
cell.RestImage.image = [UIImage imageNamed:_Images[row]];
}
cell.RestImage.layer.cornerRadius = 6;
cell.RestImage.clipsToBounds = YES;
cell.RestImage.layer.borderWidth = 1;
return cell;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
/*Cambia el nombre del boton de la izquierda sin afectar al titulo del navigation controller*/
self.navigationItem.backBarButtonItem=[[UIBarButtonItem alloc] initWithTitle: NSLocalizedString (#"Back", nil) style:UIBarButtonItemStylePlain target:nil action:nil];
if ([[segue identifier] isEqualToString:#"ShowDetails"]){
RestViewController *restviewcontroller = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
int row = [myIndexPath row];
restviewcontroller.DetailModal = #[_Title[row],_Description[row],_Images[row]];
}
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length == 0) {
//Set our boolean flag
isFiltered = NO;
} else {
//Set our boolean flag
isFiltered = YES;
}
//Alloc and init our filteredData
filteredRest = [[NSMutableArray alloc] init];
for (NSString * restTitle in _Title) {
NSRange restTitleRange = [restTitle rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (restTitleRange.location != NSNotFound) {
[filteredRest addObject:restTitle];
}
}
for (NSString * restDescription in _Description) {
NSRange restDescriptionRange = [restDescription rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (restDescriptionRange.location != NSNotFound) {
//[filteredRest addObject:restDescription];
}
}
for (NSString * restImages in _Images) {
NSRange restImagesRange = [restImages rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (restImagesRange.location != NSNotFound) {
//[filteredRest addObject:restImages];
}
}
//Reload our table view
[_RestTableView reloadData];
}
-(void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[mySearchBar resignFirstResponder];
}
#end
Finally, this is my TableViewCell.h called RestTableViewCell.h
#import <UIKit/UIKit.h>
#interface RestTableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *TitleLabel;
#property (strong, nonatomic) IBOutlet UILabel *DescriptionLabel;
#property (strong, nonatomic) IBOutlet UIImageView *RestImage;
#end
This is my problem graphically:
Obviously, you just apply the filter condition to the Title array, and not filtering the Images and Description array.
So you need to two more NSMutableArray to hold the filtering results for Images and Description.
But I recommend you to build a new Model for your results. Sample code for your reference:
Model layer: Restaurant.h
#interface Restaurant : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *desc;
#property (nonatomic, copy) NSString *image;
- (instancetype)init:(NSString *)title descripiton:(NSString *)description image:(NSString *)image;
#end
Restaurant.m
#implementation Restaurant
- (instancetype)init:(NSString *)title descripiton:(NSString *)description image:(NSString *)image {
self = [super init];
if (self != nil) {
self.title = title;
self.desc = description;
self.image = image;
}
return self;
}
#end
RestViewController.h
#interface RestViewController : UIViewController
#property (nonatomic, strong) Restaurant *DetailModal;
#end
MainTableViewController.m
#interface MainTableViewController ()
#property (nonatomic, strong) NSArray<Restaurant *> *originData;
#property (nonatomic, strong) NSMutableArray<Restaurant *> *filteredRest;
#property (nonatomic, assign) Boolean isFiltered;
#end
#implementation MainTableViewController
#synthesize mySearchBar, filteredRest, isFiltered, originData;
- (void)viewDidLoad {
[super viewDidLoad];
originData = #[
[[Restaurant alloc] init:#"80 Grados" descripiton:#"Barrio Malasaña" image:#"80_grados.png"],
[[Restaurant alloc] init:#"90 Grados" descripiton:#"Barrio Retiro" image:#"90_grados"]
];
filteredRest = [NSMutableArray new];
isFiltered = NO;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//return _Title.count;
if (isFiltered == YES) {
return filteredRest.count;
} else {
return originData.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TableCell";
RestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
if (isFiltered == YES) {
cell.TitleLabel.text = [filteredRest objectAtIndex:indexPath.row].title;
cell.DescriptionLabel.text = [filteredRest objectAtIndex:indexPath.row].desc;
cell.RestImage.image = [UIImage imageNamed:[filteredRest objectAtIndex:indexPath.row].image];
} else {
cell.TitleLabel.text = [originData objectAtIndex:indexPath.row].title;
cell.DescriptionLabel.text = [originData objectAtIndex:indexPath.row].desc;
cell.RestImage.image = [UIImage imageNamed:[originData objectAtIndex:indexPath.row].image];
}
cell.RestImage.layer.cornerRadius = 6;
cell.RestImage.clipsToBounds = YES;
cell.RestImage.layer.borderWidth = 1;
return cell;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
/*Cambia el nombre del boton de la izquierda sin afectar al titulo del navigation controller*/
self.navigationItem.backBarButtonItem=[[UIBarButtonItem alloc] initWithTitle: NSLocalizedString (#"Back", nil) style:UIBarButtonItemStylePlain target:nil action:nil];
if ([[segue identifier] isEqualToString:#"ShowDetails"]){
RestViewController *restviewcontroller = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
if (isFiltered) {
restviewcontroller.DetailModal = filteredRest[myIndexPath.row];
} else {
restviewcontroller.DetailModal = originData[myIndexPath.row];
}
}
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length == 0) {
//Set our boolean flag
isFiltered = NO;
} else {
//Set our boolean flag
isFiltered = YES;
}
//Alloc and init our filteredData
filteredRest = [[NSMutableArray alloc] init];
for (Restaurant *item in originData) {
if ([item.title containsString:searchText]) {
[filteredRest addObject:item];
}
}
//Reload our table view
[self.tableView reloadData];
}
-(void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[mySearchBar resignFirstResponder];
}
#end
With building a new model object for your information is very efficient, so you don't need to apply there filter to three array, you just need to search once, and results will be kept kept in one array.
PS: One more tips for your coding style, naming the variable, object instance with starting lowercase, and naming the class with starting uppercase is best practise when writing OC codes. e.g., TitleLabel should rename to titleLabel, etc...
Here you are filtering cell and reusing them.
You are just replacing text. You need to replace the image and data as well.
if (isFiltered == YES) {
cell.TitleLabel.text = [filteredRest objectAtIndex:indexPath.row];
} else {
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _Description[row];
cell.RestImage.image = [UIImage imageNamed:_Images[row]];
}
I am not sure but I think you have not clearly understood concept of reusable cell. You will be reusing cell which are not in the view what that means is iOS framework will give you one of the already used cell so that it doesnt have to recreate cell everytime in your case thats first cell 80Grados.
So, after you receive that cell, you replace the title but leave all other content alone. That is why you see old content with new Title Label.
What you need to do here is create a wrapper class, which will have three properties text, description and imagefile name.
#class Restaurant
#property NSString *title;
#property NString *description;
#property NSString *image;
#end
Combine your logic with this rather than treating all data as separate array. That will be extremely easy to maintain. In future you can easily fill that class with data coming from server, user entered or any otherway. Handling data in 3 different array is not very sustainable model.
So create wrapper class, fill data by creating NSMutableArray/NSMutableDictionary. Rather than putting title, fill entire wrapper object.
[filteredRest addObject: restaurantObj];
And change your code this way
Restaurant *r;
if (isFiltered == YES) {
r = [self.filteredRest objectAtIndex:indexPath.row];
} else {
r = [self.restaurantList objectAtIndex:indexPath.row]];
}
cell.TitleLabel.text = r.title;
cell.DescriptionLabel.text = r.description;
cell.RestImage.image = [UIImage imageNamed:r.image];
Let me know if any of the ideas are not clear. This is just a prototype, you will have to change your code in multiple places but would highly recommend making these changes sooner than later.
Related
I'm getting correct data after loading the page as shown in this screen
getting correct data even after performing search like this image
but after search is cancelled im getting wrong data (label should be ph:+ and Fax:+ but its displaying both as Fax:+ like this
with this code
#implementation Staffdir
#synthesize tableview,filteredContentList,searchBar1;
- (void)viewDidLoad {
searchBar1.delegate = self;
__block StaffDirectoryModel*staffdirectorymodel;
NSURL *url = [NSURL URLWithString:#"http://dev.devobal.com/GetData.aspx?Query=select%20*%20from%20tb_RHP_Staff_Directory"];
_staffdirarr=[[NSMutableArray alloc]init];
NSMutableURLRequest *req=[NSMutableURLRequest requestWithURL:url];
[[[NSURLSession sharedSession]dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSMutableArray *jsonarr=[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI
for(int i=0;i<jsonarr.count;i++){
staffdirectorymodel=[[StaffDirectoryModel alloc]init];
staffdirectorymodel.name =[[jsonarr objectAtIndex:i]objectForKey:#"Name"];
staffdirectorymodel.occupation=[[jsonarr objectAtIndex:i]objectForKey:#"Job_Title"];
staffdirectorymodel.phonelabel =[[jsonarr objectAtIndex:i]objectForKey:#"Phone"];
staffdirectorymodel.faxlabel =[[jsonarr objectAtIndex:i]objectForKey:#"Fax_Number"];
staffdirectorymodel.department =[[jsonarr objectAtIndex:i]objectForKey:#"Department"];
[_staffdirarr addObject:staffdirectorymodel];
staffdirectorymodel=nil;
}
[tableview reloadData];
});
}]resume];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
//-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
// return 1;
//}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (isSearching) {
return [searchResults count];
}else{
return _staffdirarr.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellidentifer = #"cell";
StaffDirectoryModel*staffdirectorymodel=[[StaffDirectoryModel alloc]init];
Staffcustom *cell= [tableView dequeueReusableCellWithIdentifier:cellidentifer];
if (cell == nil) {
cell = [[Staffcustom alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifer];
}
[cell.fxlabel setHidden:NO];
[cell.faxnumlabel setHidden:NO];
if (isSearching) {
staffdirectorymodel=[searchResults objectAtIndex:indexPath.row];
cell.namelabel.text = staffdirectorymodel.name;
cell.occupationlabel.text = staffdirectorymodel.occupation;
NSString*tempph=staffdirectorymodel.phonelabel;
NSString*tempfax=staffdirectorymodel.faxlabel;
if(tempph.length!=0)
{
cell.phonelabel.text = staffdirectorymodel.phonelabel;
}
else{
cell.phlabel.text=#"Fax: +";
cell.phonelabel.text = staffdirectorymodel.faxlabel;
[cell.fxlabel setHidden:YES];
[cell.faxnumlabel setHidden:YES];
}
if(tempfax.length==0)
{
[cell.faxnumlabel setHidden:YES];
[cell.fxlabel setHidden:YES];
}
if([tempph length]!=0 &&[tempfax length]!=0){
cell.faxnumlabel.text = staffdirectorymodel.faxlabel;
}
cell.Depart.text = staffdirectorymodel.department;
}
else{
staffdirectorymodel=[_staffdirarr objectAtIndex:indexPath.row];
cell.namelabel.text = staffdirectorymodel.name;
cell.occupationlabel.text = staffdirectorymodel.occupation;
NSString*tempph=staffdirectorymodel.phonelabel;
NSString*tempfax=staffdirectorymodel.faxlabel;
if(tempph.length!=0)
{
cell.phonelabel.text = staffdirectorymodel.phonelabel;
}
else{
cell.phlabel.text=#"Fax: +";
cell.phonelabel.text = staffdirectorymodel.faxlabel;
[cell.fxlabel setHidden:YES];
[cell.faxnumlabel setHidden:YES];
}
if(tempfax.length==0)
{
[cell.faxnumlabel setHidden:YES];
[cell.fxlabel setHidden:YES];
}
if([tempph length]!=0 &&[tempfax length]!=0){
cell.faxnumlabel.text = staffdirectorymodel.faxlabel;
}
cell.Depart.text = staffdirectorymodel.department;
}
cell.namelabel.textColor = [UIColor blueColor];
cell.occupationlabel.textColor=[UIColor colorWithRed:0.5 green:0.0 blue:1 alpha:1.0];
UIView * additionalSeparator = [[UIView alloc] initWithFrame:CGRectMake(0,cell.frame.size.height-1,cell.frame.size.width,0.1)];
additionalSeparator.backgroundColor = [UIColor blueColor];
[cell addSubview:additionalSeparator];
return cell;
}
//- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// isSearching = YES;
//}
-(void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)searchText
{
//[filteredContentList removeAllObjects];
if(searchText.length!=0){
isSearching=YES;
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"name contains[c] %#",
searchText];
searchResults=(NSMutableArray *)[_staffdirarr filteredArrayUsingPredicate:resultPredicate];
[tableview reloadData];
}
else{
isSearching=NO;
[tableview reloadData];
}
//
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
isSearching=YES;
[tableview reloadData];}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
isSearching=NO;
[tableview reloadData];}
and
staffdirectory model class
#interface StaffDirectoryModel : NSObject
#property (nonatomic,retain) NSString*name;
#property (nonatomic,retain) NSString*occupation;
#property (nonatomic,retain) NSString*phlabel;
#property (nonatomic,retain) NSString*phonelabel;
#property (nonatomic,retain) NSString*fxlabel;
#property (nonatomic,retain) NSString*faxlabel;
#property (nonatomic,retain) NSString*department;
staffcustom.h
#interface Staffcustom : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *fxlabel;
#property (weak, nonatomic) IBOutlet UILabel *namelabel;
#property (weak, nonatomic) IBOutlet UILabel *occupationlabel;
#property (weak, nonatomic) IBOutlet UILabel *phlabel;
#property (weak, nonatomic) IBOutlet UILabel *faxlabel;
#property (weak, nonatomic) IBOutlet UILabel *phonelabel;
#property (weak, nonatomic) IBOutlet UILabel *faxnumlabel;
#property (nonatomic,retain) IBOutlet UILabel *Depart;
#end
I have project with normal cell and working success but I want to change it with custom cell I added Cell.h and Cell.m files into my project and I need to integrate custom cell to my working cell. And last I want to show detail view title description and image (I added title codes working) My codes under
Cell.h
#import <UIKit/UIKit.h>
#interface Cell : UITableViewCell
#property (nonatomic, weak) IBOutlet UIImageView *imaj;
#property (nonatomic, weak) IBOutlet UILabel *descriptionLabel;
#property (nonatomic, weak) IBOutlet UILabel *titleLabel;
#end
Cell.m
#import "Cell.h"
#implementation Cell
#synthesize imaj = _imaj;
#synthesize descriptionLabel = _descriptionLabel;
#synthesize titleLabel = _titleLabel;
#end
ViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
#import "MBProgressHUD.h"
#import "Cell.h"
static NSString *const kConsumerKey = #"a1SNULSPtp4eLQTsTXKKSgXkYB5H4CMFXmleFvqE";
#interface MasterViewController () <UISearchBarDelegate, UISearchDisplayDelegate,MBProgressHUDDelegate>{
MBProgressHUD *HUD;
}
#property (nonatomic, assign) NSInteger currentPage;
#property (nonatomic, assign) NSInteger totalPages;
#property (nonatomic, assign) NSInteger totalItems;
#property (nonatomic, assign) NSInteger maxPages;
#property (nonatomic, strong) NSMutableArray *activePhotos;
#property (strong, nonatomic) NSMutableArray *staticDataSource;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation MasterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.activePhotos = [[NSMutableArray alloc] init];
self.searchResults = [[NSMutableArray alloc] init];
self.staticDataSource = [[NSMutableArray alloc] init];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self loadPhotos:self.currentPage];
}
#pragma mark - Table View
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// if (self.currentPage == self.maxPages
// || self.currentPage == self.totalPages
// || self.currentPage == self.totalPages
// || self.totalItems == self.photos.count) {
// return self.photos.count;
// } else if (self.tableView == self.searchDisplayController.searchResultsTableView){
// return [self.searchResults count];
//
// }
// return self.photos.count + 1;
return self.activePhotos.count + 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.currentPage != self.maxPages && indexPath.row == [self.staticDataSource count] - 1 ) {
[self loadPhotos:++self.currentPage];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if (indexPath.row == [self.activePhotos count]) {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Loading";
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
} else {
NSDictionary *photoItem = self.activePhotos[indexPath.row];
cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
// cell.textLabel.text = [self.searchResults[indexPath.row] valueForKey:#"name"];
// } else {
// NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"] ] placeholderImage:[UIImage imageNamed:#"placeholder.jpg"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
// NSLog(#"%#",self.searchResults);
return cell;
}
- (void)myTask {
// Do something usefull in here instead of sleeping ...
sleep(1.5);
}
#pragma mark UISearchDisplay delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
// [self.searchResults removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
self.activePhotos = [NSMutableArray arrayWithArray:[self.staticDataSource filteredArrayUsingPredicate:resultPredicate]];
//[self.tableData filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)loadPhotos:(NSInteger)page
{
NSString *apiURL = [NSString stringWithFormat:#"https://api.500px.com/v1/photos?feature=editors&page=%ld&consumer_key=%#",(long)page,kConsumerKey];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:apiURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error) {
NSError *jsonError = nil;
NSMutableDictionary *jsonObject = (NSMutableDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
NSLog(#"%#",jsonObject);
[self.staticDataSource addObjectsFromArray:[jsonObject objectForKey:#"photos"]];
self.currentPage = [[jsonObject objectForKey:#"current_page"] integerValue];
self.totalPages = [[jsonObject objectForKey:#"total_pages"] integerValue];
self.totalItems = [[jsonObject objectForKey:#"total_items"] integerValue];
self.activePhotos = self.staticDataSource;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}] resume];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.StoreList = [self.activePhotos objectAtIndex:indexPath.row];
}
#end
Also I uploaded working project here
http://www.filedropper.com/needcustomcell
In your
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell;
// rest of your code
}
that would do it. You have used UITableViewCell you need to replace ot with your custom cell class.
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;
}
I'm seeing a gradual build up of memory that I think might be a retain cycle.
When does this happen: Click on a custom cell that expands and injects a nib with 3 buttons into the expanded area. Clicking the cell again closes the cell, shrinks the cell's tablerow height, rotates an open indicator and removes the previously injected nib.
If I open and close the cell multiple times I see the memory gradually building up.
Any ideas what might be causing this would be greatly appreciated.
Sorry I don't have enough reputation to post photos.
Build up:
Example of retained objects(mostly Animation related):
EDIT: Using ARC and on iOS 6
MasterViewController - TableView Functions
#pragma mark - UITABLEVIEW
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.topicsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier2 = #"SRCollapsibleCellClosed";
SRCollapsibleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if (cell == nil) {
cell = [[SRCollapsibleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier2];
}
SRTopic *topic = [self.topicsArray objectAtIndex:indexPath.row];
[cell updateWithTopic:topic];
if([self isCellOpen:indexPath]){
CGAffineTransform transformation = CGAffineTransformMakeRotation(M_PI/2);
cell.arrow.transform = transformation;
if(![self hasChoiceBox:cell]){
[self insertChoiceBox:cell atIndex:indexPath];
}
} else{
CGAffineTransform transformation = CGAffineTransformMakeRotation(0);
cell.arrow.transform = transformation;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([self isCellOpen:indexPath]){
[self closeCellAtIndexPath:indexPath];
}
else{
NSIndexPath * openCell= self.openCellIndex;
NSIndexPath * newOpenCell= indexPath;
[self closeCellAtIndexPath:openCell];
[self openCellAtIndexPath:newOpenCell];
}
[tableView beginUpdates];
[tableView endUpdates];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
-(CGFloat)tableView: (UITableView*)tableView heightForRowAtIndexPath: (NSIndexPath*) indexPath {
if([indexPath isEqual:self.openCellIndex]){
return 217.0;
} else {
return 63.0;
}
}
-(void)rotateCellArrowAtIndexPath:(NSIndexPath*)indexPath willOpen:(bool)willOpen Animated:(bool)animated{
// Change Arrow orientation
SRCollapsibleCell *cell = (SRCollapsibleCell*) [self.topicsTableView cellForRowAtIndexPath:indexPath];
CGAffineTransform transformation;
if(willOpen){
transformation = CGAffineTransformMakeRotation(M_PI/2);
} else {
transformation = CGAffineTransformMakeRotation(0);
}
if(animated){
[UIView animateWithDuration:.2 delay:0 options:nil animations:^{
cell.arrow.transform = transformation;
} completion:nil];
}
else{
cell.arrow.transform = transformation;
}
}
-(BOOL)isCellOpen:(NSIndexPath *)indexPath{
return [indexPath isEqual:self.openCellIndex];
}
-(void)closeCellAtIndexPath:(NSIndexPath*)indexPath{
//NSLog(#"Cell closing");
[self rotateCellArrowAtIndexPath:indexPath willOpen:NO Animated:YES];
[self removeSRChoiceBoxFromCellAtIndexPath:indexPath];
self.openCellIndex = nil;
}
-(void)openCellAtIndexPath:(NSIndexPath*)indexPath{
[self rotateCellArrowAtIndexPath:indexPath willOpen:YES Animated:YES];
SRCollapsibleCell *cell = (SRCollapsibleCell*)[self.topicsTableView cellForRowAtIndexPath:indexPath];
[self insertChoiceBox:cell atIndex:indexPath];
self.openCellIndex = indexPath;
}
-(void)removeSRChoiceBoxFromCellAtIndexPath:(NSIndexPath *)indexPath{
SRCollapsibleCell *cell = (SRCollapsibleCell*) [self.topicsTableView cellForRowAtIndexPath:indexPath];
for(id subview in cell.SRCollapsibleCellContent.subviews){
if([subview isKindOfClass:[SRChoiceBox class]]){
SRChoiceBox *tempBox = subview;
[tempBox removeFromSuperview];
tempBox.delegate = nil;
tempBox = nil;
}
}
}
-(void)insertChoiceBox: (SRCollapsibleCell *)cell atIndex:(NSIndexPath *) indexPath
{
//SRChoiceBox *newBox = [[SRChoiceBox alloc] initWithFrame:CGRectMake(0, 0, 310, 141)];
SRChoiceBox *newBox = [[SRChoiceBox alloc] init];
SRTopic *topic = [self.topicsArray objectAtIndex:indexPath.row];
[newBox updateWithSRTopic:topic];
newBox.delegate = self;
[cell.SRCollapsibleCellContent addSubview:newBox];
cell = nil;
topic = nil;
newBox = nil;
}
-(bool)hasChoiceBox:(SRCollapsibleCell *)cell{
for(UIView *subview in cell.SRCollapsibleCellContent.subviews){
if([subview isKindOfClass:[SRChoiceBox class]]){
return true;
}
}
return false;
}
SRChoiceBox - UIView object that gets inserted into cell
//.h
#protocol SRChoiceBoxDelegate <NSObject>
-(void)positionWasChoosen: (NSString *)choice topicId: (NSNumber *)topicId;
#end
#interface SRChoiceBox : UIView
-(id) initWithLabel: (NSDictionary *)labels andTopicID: (NSNumber *)topicId andFrame:(CGRect)frame;
#property (nonatomic, weak) IBOutlet UIView *SRChoiceBox;
#property (nonatomic, strong) NSNumber *SRTopicId;
#property (nonatomic, weak) id<SRChoiceBoxDelegate> delegate;
#property (weak, nonatomic) IBOutlet UILabel *agreeCount;
#property (weak, nonatomic) IBOutlet UILabel *disagreeCount;
#property (weak, nonatomic) IBOutlet UILabel *observeCount;
-(IBAction)buttonPress:(id)sender;
-(void)updateWithSRTopic:(SRTopic *)topic;
....
//.m
-(id)init{
self = [super init];
if (self) {
UINib *nib = [UINib nibWithNibName:#"SRChoiceBox" bundle:nil];
NSArray *q = [nib instantiateWithOwner:self options:nil];
[self addSubview:q[0]];
}
return self;
}
-(void)updateWithSRTopic:(SRTopic *)topic
{
self.SRTopicId = topic.topicId;
self.agreeCount.text = [NSString stringWithFormat: #"%#",topic.agreeDebaters];
self.disagreeCount.text = [NSString stringWithFormat: #"%#",topic.disagreeDebaters];
self.observeCount.text = [NSString stringWithFormat: #"%#",topic.observers];
}
- (IBAction)buttonPress:(id) sender {
int tag = [sender tag];
switch (tag) {
case 0:
[self.delegate positionWasChoosen:#"agree" topicId:self.SRTopicId];
break;
case 1:
[self.delegate positionWasChoosen: #"disagree" topicId:self.SRTopicId];
break;
case 2:
[self.delegate positionWasChoosen: #"observe" topicId:self.SRTopicId];
break;
default:
break;
}
}
- (void)dealloc
{
self.SRChoiceBox =nil;
self.SRTopicId=nil;
self.delegate=nil;
self.agreeCount=nil;
self.disagreeCount=nil;
self.observeCount=nil;
//NSLog(#"choicebox deallocated: %#", self);
}
SRCollapsibleCell -- Reusable cell
//.h
#interface SRCollapsibleCell : UITableViewCell
#property (strong) NSNumber *topicId;
#property (strong) NSDictionary *topicStats;
#property (weak, nonatomic) IBOutlet UILabel *title;
#property (weak, nonatomic) IBOutlet UILabel *subtitle;
#property (weak, nonatomic) IBOutlet UILabel *agreeDebaters;
#property (weak, nonatomic) IBOutlet UILabel *disagreeDebaters;
#property (weak, nonatomic) IBOutlet UILabel *observers;
#property (weak, nonatomic) IBOutlet UIImageView *arrow;
#property (weak, nonatomic) IBOutlet UIView *SRCollapsibleCellContent;
//-(void)updateWithTopic:(NSDictionary *) stats;
-(void)formatTitle:(NSString *)title;
-(void)updateWithTopic: (SRTopic *)topic;
#end
//.m
#implementation SRCollapsibleCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
-(void)formatTitle:(NSString *)title{
if(title.length<30){
self.title.text= title;
self.subtitle.text =#"";
} else {
NSArray *splitString = [self splitString:title maxCharacters:30];
self.title.text = splitString[0];
self.subtitle.text = splitString[1];
splitString = nil;
title = nil;
}
}
////http://www.musicalgeometry.com/?p=1197
- (NSArray *)splitString:(NSString*)str maxCharacters:(NSInteger)maxLength {
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:1];
NSArray *wordArray = [str componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSInteger numberOfWords = [wordArray count];
NSInteger index = 0;
NSInteger lengthOfNextWord = 0;
while (index < numberOfWords && tempArray.count<2) {
NSMutableString *line = [NSMutableString stringWithCapacity:1];
while ((([line length] + lengthOfNextWord + 1) <= maxLength) && (index < numberOfWords)) {
lengthOfNextWord = [[wordArray objectAtIndex:index] length];
[line appendString:[wordArray objectAtIndex:index]];
index++;
if (index < numberOfWords) {
[line appendString:#" "];
}
}
[tempArray addObject:line];
NSMutableString *subtitle = [NSMutableString stringWithCapacity:1];
while(index<numberOfWords){
[subtitle appendString:[wordArray objectAtIndex:index]];
[subtitle appendString:#" "];
index++;
}
[tempArray addObject:subtitle];
break;
}
return tempArray;
}
//Breaks MVC but it makes the MasterVC cleaner
-(void)updateWithTopic: (SRTopic *)topic
{
[self formatTitle:topic.title];
self.topicId = topic.topicId;
self.agreeDebaters.text = [NSString stringWithFormat:#"%#",topic.agreeDebaters];
self.disagreeDebaters.text = [NSString stringWithFormat:#"%#", topic.disagreeDebaters];
self.observers.text = [NSString stringWithFormat:#"%#", topic.observers];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
}
#end
My app has a tabbarcontroller with a UIViewController (FirstViewController-calling it mapVC) with a mapview and a UITableViewController (SecondViewController-calling it tableVC). The app fetches data from web and puts it into CD-db and each VC executes a fetch to the db. Each entity is named Holiday (dont ask) and it has a lat and long property.
Here is the UITabBarController subclass which attempts to set the mapVC as datasource to tableVC:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view.
FirstViewController *mapVC;
SecondViewController *tableVC;
for(UIViewController *anyVC in self.viewControllers)
{
if([anyVC.class isKindOfClass:[SecondViewController class]]){
tableVC = (SecondViewController *)anyVC;
} else if ([anyVC.class isKindOfClass:[FirstViewController class]]){
mapVC = (FirstViewController *)anyVC;
}
}
tableVC.tableView.dataSource = mapVC;
tableVC.tableView.delegate = mapVC;
}
Here are the relevant parts of mapVC:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "SecondViewController.h"
#define METERS_PER_MILE 2609.344
#interface FirstViewController : UIViewController <MKMapViewDelegate, UITableViewDataSource, UITableViewDelegate>{
BOOL _doneInitialZoom;
}
#property (strong, nonatomic) IBOutlet MKMapView *_mapView;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshButton;
#property (nonatomic, strong) NSString *entityName;
#property (strong, nonatomic) CLLocation *userLocation;
- (IBAction)refreshTapped:(id)sender;
-(void)showDetailView;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
#end
and its implementation:
#import "FirstViewController.h"
#import "Holiday.h"
#import "MyLocation.h"
#import "SDCoreDataController.h"
#import "MyTabBarController.h"
#import "TableViewCell.h"
- (void)loadRecordsFromCoreData {
[self.managedObjectContext performBlockAndWait:^{
[self.managedObjectContext reset];
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:self.entityName];
[request setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES]]];
self.farSiman = [self.managedObjectContext executeFetchRequest:request error:&error];
}];
NSLog(#"self.farSiman on launch = %#", self.farSiman);
}
- (void)plotStorePositions:(NSString *)responseString {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
NSLog(#"Dictionary is %#", self.farSiman);
for (Holiday * holidayObject in self.farSiman) {
NSString * latitude = holidayObject.latitude;
NSString * longitude = holidayObject.longitude;
NSString * storeDescription = holidayObject.name;
NSString * address = holidayObject.address;
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
MyLocation *annotation = [[MyLocation alloc] initWithName:storeDescription address:address coordinate:coordinate distance:0];
//
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
//[(MyLocation*)[view annotation] coordinate].latitude longitude:[(MyLocation*)[view annotation] coordinate].longitude]];
self.userLocation = [[CLLocation alloc] initWithLatitude:self._mapView.userLocation.coordinate.latitude longitude:self._mapView.userLocation.coordinate.longitude];
NSLog(#"PLOT>>userLocation is %#", userLocation);
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
annotation.distance = calculatedDistance/1000;
NSLog(#"PLOT>>Distance to pin %4.0f", annotation.distance);
//
[_mapView addAnnotation:annotation];
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.farSiman count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = nil;
// Check to see whether the normal table or search results table is being displayed and set the Candy object from the appropriate array
NSLog(#"Already in CFRAIP");
static NSString *CellIdentifier = #"HolidayCell";
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton;
}
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Holiday *holiday = [self.farSiman objectAtIndex:indexPath.row];
cell.nameLabel.text = holiday.name;
//cell.dateLabel.text = holiday.latitude;
cell.dateLabel.text = [[self calculateDistanceForLat:[holiday.latitude doubleValue] Long:[holiday.longitude doubleValue]] stringValue];
if (holiday.image != nil) {
UIImage *image = [UIImage imageWithData:holiday.image];
cell.imageView.image = image;
} else {
cell.imageView.image = nil;
}
return cell;
}
// Add new method above refreshTapped
- (NSNumber*)calculateDistanceForLat:(double)lati Long:(double)longi {
double distancia;
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:lati longitude:longi];
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
//test locations
NSLog(#"pinLocations is %#, userLocation is %#", pinLocation, self.userLocation);
distancia = calculatedDistance/1000;
return [NSNumber numberWithDouble:distancia];
}
The plotStoreLocations method is the only method call from a UIButton in the mapVC toolbar.
As for tableVC (SecondViewController)
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface SecondViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, strong) NSArray *dates;
#property (nonatomic, strong) NSString *entityName;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshButton;
#property (strong,nonatomic) NSMutableArray *filteredResultsArray;
#property (strong,nonatomic) IBOutlet UISearchBar *resultsSearchBar;
#property (strong, nonatomic) CLLocation *userLocation;
- (IBAction)refreshButtonTouched:(id)sender;
And its implementation:
- (void)loadRecordsFromCoreData {
[self.managedObjectContext performBlockAndWait:^{
[self.managedObjectContext reset];
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:self.entityName];
[request setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES]]];
self.dates = [self.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"self.dates==%#",self.dates);
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [filteredResultsArray count];
} else {
return [self.dates count];
}
//return [self.dates count];
}
// Add new method above refreshTapped
- (NSNumber*)calculateDistanceForLat:(double)lati Long:(double)longi {
double distancia;
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:lati longitude:longi];
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
//test locations
NSLog(#"pinLocations is %#, userLocation is %#", pinLocation, self.userLocation);
distancia = calculatedDistance/1000;
return [NSNumber numberWithDouble:distancia];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
static NSString *CellIdentifier = #"HolidayCell";
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Holiday *holiday = [filteredResultsArray objectAtIndex:indexPath.row];
NSLog(#"the holiday is %#", holiday.name);
cell.nameLabel.text = holiday.name;
} else {
static NSString *CellIdentifier = #"HolidayCell";
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Holiday *holiday = [self.dates objectAtIndex:indexPath.row];
cell.nameLabel.text = holiday.name;
cell.dateLabel.text = [[self calculateDistanceForLat:[holiday.latitude doubleValue] Long:[holiday.longitude doubleValue]] stringValue];
if (holiday.image != nil) {
UIImage *image = [UIImage imageWithData:holiday.image];
cell.imageView.image = image;
} else {
cell.imageView.image = nil;
}
}
return cell;
}
Finally MyLocation:
import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyLocation : NSObject <MKAnnotation> {
NSString *_name;
NSString *_address;
CLLocationCoordinate2D _coordinate;
}
#property (copy) NSString *name;
#property (copy) NSString *address;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (assign) float distance;
- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate distance:(float)distance;
#end
#import "MyLocation.h"
#implementation MyLocation
#synthesize name = _name;
#synthesize address = _address;
#synthesize coordinate = _coordinate;
#synthesize distance = _distance;
- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate distance:(float)distance{
if ((self = [super init])) {
_name = [name copy];
_address = [address copy];
_coordinate = coordinate;
_distance = distance;
}
return self;
}
- (NSString *)title {
if ([_name isKindOfClass:[NSNull class]])
return #"Unknown charge";
else
return _name;
}
- (NSString *)subtitle {
return [NSString stringWithFormat:#"A %0.2f Kms", _distance];
//return _address;
}
Specific Questions:
1) If I added the cFRAIP (noris & nosit) method in mapVC (the new datasource) do I need to remove it from tableVC?
2) If I remove cFRAIP and the other 2 (noris and nosit) methods from tableVC, it crashes because there is no datasource. So the tabbarcontroller assignment of datasource doesnt seem to be working.
3) Finally if I have to remove cFRAIP from tableVC, I will lose my ability to UISearchBar the tableview. Or am I wrong?
When I run the app, the mapVC is the selected vc. The UIButton in the toolbar that calls plotStoreLocations in mapVC is greyed out until the web fetch finishes. At this point the console logs the self.farsiman locations which are Holiday entities. And I can see all the entities log into the console.
When I click the plot button, the userLocation is logged correctly as specified in the plotStorePositions and the distance value for each annotation is correct. So the distance for each MKAnnotation is calculated correctly.
When I switch to the tableVC tab, the new self.dates array is logged in the console (because I currently have the tableVC do another CD-db fetch. And I get this for each pin location:
Already in CFRAIP pinLocations is <+15.50288611,-88.02716389> +/-
0.00m (speed -1.00 mps / course -1.00) # 1/24/13, 8:20:39 PM Central Standard Time, userLocation is (null)
which is called from the calculateDistanceForLat which is called by the CFRAIP. And every distance in the cell detail is -0.001.
You definitely need to make mapVC the datasource and delegate. If UITableViewController isn't letting you assign it to something other that itself you might want to consider using a regular UIViewController and drop a UITableView on to it. If you set it up as a property called tableview the code in tabbarcontroller that does tableVC.tableView.dataSource = mapVC; will still work.
Yes you can and should remove all tableview delegate and datasource code from your tableVC, if it is calling those then it isn't calling the ones in the mapVC which is what you want.
It's probably giving you the wrong distance because tableVC doesn't have the user's location to measure from. Another good reason for having the table datasource attached to the map.