I'm having trouble converting my codes from xib format to storyboard for my iBeacon application. There is nothing showing on the table cells. All help is appreciated!
#import "RangingViewController.h"
#import "Default.h"
#implementation RangingViewController
{
NSMutableDictionary *_beacons;
CLLocationManager *_locationManager;
NSMutableArray *_rangedRegions;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if(self)
{
_beacons = [[NSMutableDictionary alloc] init];
// This location manager will be used to demonstrate how to range beacons.
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
// CoreLocation will call this delegate method at 1 Hz with updated range information.
// Beacons will be categorized and displayed by proximity.
[_beacons removeAllObjects];
NSArray *unknownBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"proximity = %d", CLProximityUnknown]];
if([unknownBeacons count])
[_beacons setObject:unknownBeacons forKey:[NSNumber numberWithInt:CLProximityUnknown]];
NSArray *immediateBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"proximity = %d", CLProximityImmediate]];
if([immediateBeacons count])
[_beacons setObject:immediateBeacons forKey:[NSNumber numberWithInt:CLProximityImmediate]];
NSArray *nearBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"proximity = %d", CLProximityNear]];
if([nearBeacons count])
[_beacons setObject:nearBeacons forKey:[NSNumber numberWithInt:CLProximityNear]];
NSArray *farBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"proximity = %d", CLProximityFar]];
if([farBeacons count])
[_beacons setObject:farBeacons forKey:[NSNumber numberWithInt:CLProximityFar]];
[self.rangeTable reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
// Start ranging when the view appears.
[_rangedRegions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CLBeaconRegion *region = obj;
[_locationManager startRangingBeaconsInRegion:region];
}];
}
- (void)viewDidDisappear:(BOOL)animated
{
// Stop ranging when the view goes away.
[_rangedRegions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CLBeaconRegion *region = obj;
[_locationManager stopRangingBeaconsInRegion:region];
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Ranging";
// Populate the regions we will range once.
_rangedRegions = [NSMutableArray array];
[[Default sharedDefaults].supportedProximityUUIDs enumerateObjectsUsingBlock:^(id uuidObj, NSUInteger uuidIdx, BOOL *uuidStop) {
NSUUID *uuid = (NSUUID *)uuidObj;
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:[uuid UUIDString]];
[_rangedRegions addObject:region];
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)rangeTable
{
return _beacons.count;
}
- (NSInteger)tableView:(UITableView *)rangeTable numberOfRowsInSection:(NSInteger)section
{
NSArray *sectionValues = [_beacons allValues];
return [[sectionValues objectAtIndex:section] count];
}
- (NSString *)tableView:(UITableView *)rangeTable titleForHeaderInSection:(NSInteger)section
{
NSString *title = nil;
NSArray *sectionKeys = [_beacons allKeys];
// The table view will display beacons by proximity.
NSNumber *sectionKey = [sectionKeys objectAtIndex:section];
switch([sectionKey integerValue])
{
case CLProximityImmediate:
title = #"Immediate";
break;
case CLProximityNear:
title = #"Near";
break;
case CLProximityFar:
title = #"Far";
break;
default:
title = #"Unknown";
break;
}
return title;
}
- (UITableViewCell *)tableView:(UITableView *)rangeTable cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
_rangeCell = [rangeTable dequeueReusableCellWithIdentifier:identifier];
if (_rangeCell == nil)
{
_rangeCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
_rangeCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Display the UUID, major, minor and accuracy for each beacon.
NSNumber *sectionKey = [[_beacons allKeys] objectAtIndex:indexPath.section];
CLBeacon *beacon = [[_beacons objectForKey:sectionKey] objectAtIndex:indexPath.row];
_rangeCell.textLabel.text = [beacon.proximityUUID UUIDString];
_rangeCell.detailTextLabel.text = [NSString stringWithFormat:#"Major: %#, Minor: %#, Acc: %.2fm", beacon.major, beacon.minor, beacon.accuracy];
return _rangeCell;
}
#end
I have declared the TableViewCell as _rangeCell and the TableView as rangeView.
In Storyboard
-First create a prototype cell without any subclass
-Remove all hooked links with the objects
-Set the cell Identifier
-Use my code below and let me know if the problem persist:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Set the indexes according to your UIControls Listing
UILabel *lblMainText = (UILabel *)[cell.contentView.subviews objectAtIndex:0];
UILabel *lblDetailText = (UILabel *)[cell.contentView.subviews objectAtIndex:3];
UILabel *lblDetailText2 = (UILabel *)[cell.contentView.subviews objectAtIndex:1];
UILabel *lblDetailText3 = (UILabel *)[cell.contentView.subviews objectAtIndex:2];
UIButton *btnFavourite = (UIButton *)[cell.contentView.subviews objectAtIndex:6];
UIButton *btnMore = (UIButton *)[cell.contentView.subviews objectAtIndex:4];
//cell.backgroundColor = [UIColor clearColor];
lblMainText.text = [NSString stringWithFormat:#"%#",#"someValue"];
lblDetailText.text= [NSString stringWithFormat:#"%#",#"someValue"];
lblDetailText2.text= [NSString stringWithFormat:#"%#",#"someValue"];
lblDetailText3.text= [NSString stringWithFormat:#"Marks",#"someValue"];
btnFavourite.tag = indexPath.row;
[btnFavourite addTarget:self
action:#selector(callMethod:) forControlEvents:UIControlEventTouchUpInside];
btnFavourite.tag = indexPath.row;
return cell;
}
Related
I have a UITableView which shows all of the songs in the Music Library, which works fine. However, when I start typing in the search bar to narrow down the search results, the app crashes immediately. Like as soon as I press one letter on the keyboard, it crashes. I've tried reworking my textDidChange: method but it always crashes, no matter what. I'm not sure what I'm doing wrong, could anyone help? Thanks.
Header:
#interface PTTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (strong,nonatomic)UITableView* tableView;
#property (strong,nonatomic)UISearchBar* searchBar;
#end
ViewController.m:
#import "PTTableViewController.h"
#implementation PTTableViewController
MPMediaQuery *songsQuery;
NSArray *songsArray;
NSMutableArray *filteredArray;
NSMutableArray *songTitlesArray;
-(void)viewDidLoad{
[super viewDidLoad];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width - 75,150) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:_tableView];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,44)];
self.searchBar.delegate = self;
self.searchBar.placeholder = #"Search";
self.tableView.tableHeaderView = self.searchBar;
songsQuery = [MPMediaQuery songsQuery];
songsArray = [songsQuery items];
songTitlesArray = [[NSMutableArray alloc] init];
for (MPMediaItem *item in songsArray) {
[songTitlesArray addObject:[item valueForProperty:MPMediaItemPropertyTitle]];
}
filteredArray = [[NSMutableArray alloc] init];
filteredArray = [songTitlesArray copy];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return filteredArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.textLabel.text = [filteredArray objectAtIndex:indexPath.row];
return cell;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
NSLog(#"search changed");
[self performSelectorInBackground:#selector(helper) withObject:nil];
}
-(void)helper{
[filteredArray removeAllObjects];
if ([self.searchBar.text isEqualToString:#""]){
filteredArray = [songTitlesArray copy];
} else {
for (NSString *object in songTitlesArray){
if ([object rangeOfString:self.searchBar.text].location == NSNotFound){
NSLog(#"string not found");
} else {
NSLog(#"string found");
[filteredArray addObject:object];
}
}
} [self.tableView reloadData];
}
#end
First...
You're getting a crash because you do this:
filteredArray = [songTitlesArray copy];
Which assigns a non-mutable copy of songTitlesArray to filteredArray.
When you then try to do this:
[filteredArray removeAllObjects];
you're trying to mutate a non-mutable object.
You can fix this (first) problem in a couple ways...
One way is to change all instances of copy to mutableCopy:
filteredArray = [songTitlesArray mutableCopy];
However, using your exact code, you'll introduce new crashes because you'll be executing UI actions on a background thread.
Next... Are you sure you're getting "keyboard lag"?
You may want to change a few things:
make _filteredArray non-mutable
instead of calling removeAllObjects simply re-create the array
use filteredArrayUsingPredicate instead of looping through and comparing each string
Give this a try - because I don't have your MPMediaQuery I'll generate 500 strings as "song titles":
#interface PTTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (strong,nonatomic)UITableView* tableView;
#property (strong,nonatomic)UISearchBar* searchBar;
#end
#implementation PTTableViewController
//MPMediaQuery *songsQuery;
NSArray *songsArray;
NSArray *filteredArray;
NSMutableArray *songTitlesArray;
-(void)viewDidLoad{
[super viewDidLoad];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width - 75,150) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:_tableView];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,44)];
self.searchBar.delegate = self;
self.searchBar.placeholder = #"Search";
self.tableView.tableHeaderView = self.searchBar;
// songsQuery = [MPMediaQuery songsQuery];
// songsArray = [songsQuery items];
//
// songTitlesArray = [[NSMutableArray alloc] init];
// for (MPMediaItem *item in songsArray) {
// [songTitlesArray addObject:[item valueForProperty:MPMediaItemPropertyTitle]];
// }
// filteredArray = [[NSMutableArray alloc] init];
// filteredArray = [songTitlesArray copy];
// I don't have your Query, so let's create a 500 element string array,
// using the row number + one of the following words in each entry
NSString *samples = #"These are some random words for the song titles.";
// So, we'll have:
// "Row: 0 These"
// "Row: 1 are"
// "Row: 2 some"
// "Row: 3 random"
// "Row: 4 words"
// "Row: 5 for"
// "Row: 6 the"
// "Row: 7 song"
// "Row: 8 titles."
// "Row: 9 These"
// "Row: 10 are"
// "Row: 11 some"
// "Row: 12 random"
// "Row: 13 words"
// "Row: 14 for"
// ... etc
NSArray *a = [samples componentsSeparatedByString:#" "];
songTitlesArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 500; i++) {
NSString *w = a[i % [a count]];
NSString *s = [NSString stringWithFormat:#"Row: %d %#", i, w];
[songTitlesArray addObject:s];
}
filteredArray = [songTitlesArray copy];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return filteredArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.textLabel.text = [filteredArray objectAtIndex:indexPath.row];
return cell;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
[self helper];
}
-(void)helper{
if ([self.searchBar.text isEqualToString:#""]){
filteredArray = [songTitlesArray copy];
} else {
NSString *w = [NSString stringWithFormat:#"SELF contains[c] '%#'", self.searchBar.text];
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:w];
filteredArray = [songTitlesArray filteredArrayUsingPredicate:sPredicate];
}
[self.tableView reloadData];
}
#end
As a side note, you've done a nice job of demonstrating one of the (many) reasons NOT to try and develop on a jailbroken device.
I'm trying to implement a UISearchBar in a custom UITableViewController and done programmatically (not using IB). I got the search function to work and return the correct fields, but it is displaying the searched cells over the full list cells:
As you can see, the new searched field is scrollable and selectable. Its just not removing the old cells.
here is my .h file:
#interface TestTableViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate>
#property (strong, nonatomic) NSArray *boundaries;
#end
.m file:
#import "TestTableViewController.h"
#import "Boundary.h"
#interface TestTableViewController ()
#property (strong, nonatomic) UISearchDisplayController *searchController;
#property (strong, nonatomic) NSMutableArray *filteredBoundaries;
#end
#implementation TestTableViewController
-(instancetype) initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
self.filteredBoundaries = [NSMutableArray array];
}
return self;
}
-(void)viewDidLoad {
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:TRUE selector:#selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors = #[sortDescriptor];
self.boundaries = [self.boundaries sortedArrayUsingDescriptors:sortDescriptors];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
searchBar.delegate = self;
searchBar.placeholder = #"Search Fields";
searchBar.showsCancelButton = TRUE;
self.tableView.tableHeaderView = searchBar;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
}
// ------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Setup Filter Data Source
-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope {
[self.filteredBoundaries removeAllObjects];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
self.filteredBoundaries = [NSMutableArray arrayWithArray:[self.boundaries filteredArrayUsingPredicate:predicate]];
}
// ------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Table view data source
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchDisplayController.searchResultsTableView) {
return self.filteredBoundaries.count;
}
else {
return self.boundaries.count;
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
Boundary *boundary = [self.filteredBoundaries objectAtIndex:indexPath.row];
cell.textLabel.text = boundary.name;
cell.textLabel.textColor = [UIColor blackColor];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
else {
Boundary *boundary = [self.boundaries objectAtIndex:indexPath.row];
cell.textLabel.text = boundary.name;
cell.textLabel.textColor = [UIColor blackColor];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.userInteractionEnabled = TRUE;
}
return cell;
}
// ------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark UISearchDisplayController Delegates
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString scope:[[self.searchController.searchBar scopeButtonTitles] objectAtIndex:self.searchController.searchBar.selectedScopeButtonIndex]];
return TRUE;
}
#end
And how I call the table view:
TestTableViewController *tableViewController = [[TestTableViewController alloc] initWithStyle:UITableViewStylePlain];
tableViewController.boundaries = [group.boundaries allObjects];
tableViewController.contentSizeForViewInPopover = POPOVER_SIZE;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableViewController];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
navController.navigationBar.barStyle = UIBarStyleBlack;
self.myPopoverController = [[UIPopoverController alloc] initWithContentViewController:navController];
self.myPopoverController.delegate = self;
[self.myPopoverController presentPopoverFromRect:button.frame inView:button.superview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Any ideas what I might be doing wrong or missing?
The problem is that UISearchDisplayController is using another UITableView rather than the view controller's own. You can verify that by logging tableView in -tableView:cellForRowAtIndexPath:.
You can use a UISearchBar without a UISearchDisplayController, to have more control over search and display logic.
Also, if your app doesn't support any version prior to iOS 8, consider using UISearchController. I haven't tried it but it seems to give you more control. Check a sample UISearchDisplayControllerhas been deprecated in iOS 8.
Try this
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 41.0)];
[self.view addSubview:searchBar];
searchBar.delegate=self;
- (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 (isSearching) {
return [filteredContentList count];
}
else {
return [titlearray count];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
if (isSearching)
{
cell.nameLabel.text = [filteredContentList objectAtIndex:indexPath.row];
cell.thumbnailImageView.image =[filteredImgArray objectAtIndex:indexPath.row];
}
else
{
cell.thumbnailImageView.image = [imagearray objectAtIndex:indexPath.row];
cell.nameLabel.text = [titlearray objectAtIndex:indexPath.row];
}
return cell;
}
- (void)searchTableList {
NSString *searchString = searchBar.text;
for (int i=0; i<titlearray.count; i++) {
NSString *tempStr=[titlearray objectAtIndex:i];
NSComparisonResult result = [tempStr compare:searchString options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchString length])];
if (result == NSOrderedSame)
{
[filteredContentList addObject:tempStr];
[filteredImgArray addObject:[imagearray objectAtIndex:i]];
}
}
}
#pragma mark - Search Implementation
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
isSearching = YES;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSLog(#"Text change - %d",isSearching);
//Remove all objects first.
[filteredContentList removeAllObjects];
[filteredImgArray removeAllObjects];
if([searchText length] != 0) {
isSearching = YES;
[self searchTableList];
//tblContentList.hidden=NO;
}
else {
isSearching = NO;
// tblContentList.hidden=YES;
}
[tblContentList reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
NSLog(#"Cancel clicked");
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSLog(#"Search Clicked");
[self searchTableList];
}
I hope it's help for you
You should implement correct datasource.
Create new array of items for filtered data for first.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger count = ([filteredItems count] > 0) ? [filteredItems count] : [self.allItems count];
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier: #"id"];
MyCustomItem *item = ([filteredItems count] > 0) ? filteredItems[indexPath.row] : self.allItems[indexPath.row];
[self configureCell:cell forItem:item];
return cell;
}
Configure searching:
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchText = searchController.searchBar.text;
if ([searchText length] == 0)
{
[filteredItems removeAllObjects];
[self.tableView reloadData];
return;
}
NSMutableArray *searchResults = [self.allItems mutableCopy];
// SKIP ALL BODY OF SEARCHING
filteredPeoples = searchResults;
[self.tableView reloadData];
}
Will work pretty.
IOS 8 delegate has been deprecated not sure if that's the problem.
The method
here's [a link]https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UISearchDisplayDelegate_Protocol/index.html#//apple_ref/occ/intfm/UISearchDisplayDelegate/searchDisplayControS 8 delegate has been deprecated not sure if that's the problem.
The method
try this property instead
#property(nonatomic, assign) id< UISearchResultsUpdating > searchResultsUpdater
another better link [a link]https://developer.apple.com/library/ios/samplecode/TableSearch_UISearchController/Listings/TableSearch_obj_c_TableSearch_APLResultsTableController_m.html
I am a newbie on iOS Development and I am sorting out how am I going to have a table view with a checkmark on the settings bundle like this:
Is there any way to do it? Or is this just only available for specific iOS approved apps?
Looking forward for an answer. Thanks.
You can achieve this through Multi Value Element. When the user taps a preference containing a multi-value element, the Settings application displays a new page with the possible values to choose from. Google it for the tutorials if needed (Multivalue option for the settings bundle).
Here are the details : PSMultiValueSpecifier
If I am guessing right and you want to know how to display settings outside your app and in the iOS Settings, then check out this tutorial. It should get you started.
Taken out of the link below:
I have been searching around and couldn't find a boilerplate solution so created my own code for doing this. It supports the setting types Title, Group, Text Field, Multi Value and Toggle Switch.
It does NOT SUPPORT Slider.
This solution does support portrait AND landscape mode and can also handle changing over device orientations.
First off all I'm assuming that you are using the following code to read out your default values from the Settings.bundle.
- (void) registerDefaultsFromSettingsBundle
{
NSLog(#"Registering default values from Settings.bundle");
NSUserDefaults * defs = [NSUserDefaults standardUserDefaults];
[defs synchronize];
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource: #"Settings" ofType: #"bundle"];
if(!settingsBundle)
{
NSLog(#"Could not find Settings.bundle");
return;
}
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent: #"Root.plist"]];
NSArray *preferences = [settings objectForKey: #"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for (NSDictionary *prefSpecification in preferences)
{
NSString *key = [prefSpecification objectForKey:#"Key"];
if (key)
{
// check if value readable in userDefaults
id currentObject = [defs objectForKey: key];
if (currentObject == nil)
{
// not readable: set value from Settings.bundle
id objectToSet = [prefSpecification objectForKey: #"DefaultValue"];
[defaultsToRegister setObject: objectToSet forKey: key];
NSLog(#"Setting object %# for key %#", objectToSet, key);
}
else
{
// already readable: don't touch
NSLog(#"Key %# is readable (value: %#), nothing written to defaults.", key, currentObject);
}
}
}
[defs registerDefaults: defaultsToRegister];
[defs synchronize];
}
Okay now you'll need 2 classes. SettingsTableViewController and MultiValueTableViewController.
SettingsTableViewController.h
//
// SettingsTableViewController.h
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
//
#import <UIKit/UIKit.h>
#import "MultiValueTableViewController.h"
#interface SettingsTableViewController : UITableViewController <MultiValueDelegate> {
NSMutableArray *labelViews;
NSMutableArray *textViews;
NSMutableArray *settingsKeys;
NSMutableArray *settingsTableSections;
NSMutableArray *settingsTableData;
}
#end
SettingsTableViewController.m
//
// SettingsTableViewController.m
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
////
#import "SettingsTableViewController.h"
#define labelCGRectX 25
#define labelCGRectY 25
#define labelCGRectWidth 140
#define labelCGRectHeight 21
#define typeGroup #"PSGroupSpecifier"
#define typeTitle #"PSTitleValueSpecifier"
#define typeToggleSwitch #"PSToggleSwitchSpecifier"
#define typeMultiValue #"PSMultiValueSpecifier"
#define typeTextField #"PSTextFieldSpecifier"
#interface SettingsTableViewController ()
#end
#implementation SettingsTableViewController
- (id)initWithStyle: (UITableViewStyle)style
{
self = [super initWithStyle: style];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Track rotation changes
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(deviceOrientationDidChange) name: UIDeviceOrientationDidChangeNotification object: nil];
// Avoid tab bar to overlap tableview
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
// Custom initialization
labelViews = [NSMutableArray arrayWithObjects: nil];
textViews = [NSMutableArray arrayWithObjects: nil];
settingsTableSections = [NSMutableArray arrayWithObjects: nil];
settingsTableData = [NSMutableArray arrayWithObjects: nil];
settingsKeys = [NSMutableArray arrayWithObjects: nil];
NSLog(#"Created arrays");
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource: #"Settings" ofType: #"bundle"];
if(!settingsBundle) {
NSLog(#"Could not find Settings.bundle");
} else {
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent: #"Root.plist"]];
NSArray *preferences = [settings objectForKey: #"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity: [preferences count]];
for (NSDictionary *prefSpecification in preferences) {
NSLog(#"%#", prefSpecification);
NSString *title = [prefSpecification objectForKey: #"Title"];
NSString *type = [prefSpecification objectForKey: #"Type"];
if([type isEqualToString: typeGroup]) {
// Create new section
[settingsTableSections addObject: title];
NSMutableArray *newSection = [NSMutableArray arrayWithObjects: nil];
[settingsTableData addObject: newSection];
} else {
// Add specification to last section
[[settingsTableData objectAtIndex: ([settingsTableData count] - 1)] addObject:prefSpecification];
}
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
// Return the number of sections.
return [settingsTableSections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[settingsTableData objectAtIndex: section] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [settingsTableSections objectAtIndex: section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get the dictionary item
NSDictionary *prefSpecification = [[settingsTableData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];
NSString *title = [prefSpecification objectForKey: #"Title"];
NSString *key = [prefSpecification objectForKey: #"Key"];
NSString *type = [prefSpecification objectForKey: #"Type"];
// Define cell
UITableViewCell *cell;
// Keep tag of keys
[settingsKeys addObject: key];
int tag = [settingsKeys count] - 1;
if([type isEqualToString: typeTitle]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Set title
cell.textLabel.text = title;
// Add label
UILabel *labelView = [[UILabel alloc] initWithFrame: CGRectMake(labelCGRectX, labelCGRectY, labelCGRectWidth, labelCGRectHeight)];
labelView.text = [[NSUserDefaults standardUserDefaults] objectForKey: key];
labelView.textAlignment = NSTextAlignmentRight;
labelView.textColor = [UIColor grayColor];
cell.accessoryView = labelView;
}
if([type isEqualToString: typeToggleSwitch]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Set title
cell.textLabel.text = title;
// Add switch
UISwitch *switchView = [[UISwitch alloc] initWithFrame: CGRectZero];
cell.accessoryView = switchView;
switchView.tag = tag;
[switchView setOn: [[[NSUserDefaults standardUserDefaults] objectForKey: key] boolValue] animated: NO];
// Connect action to switch
[switchView addTarget: self action: #selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
}
if([type isEqualToString: typeTextField]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
int frameSize = self.view.frame.size.width;
UITextField *textField = [[UITextField alloc] initWithFrame: CGRectMake(15, 10, frameSize,labelCGRectHeight)];
textField.tag = tag;
textField.text = [[NSUserDefaults standardUserDefaults] objectForKey: key];
[textField addTarget: self
action: #selector(textFieldChanged:)
forControlEvents: UIControlEventEditingChanged];
[cell.contentView addSubview: textField];
// Tract text field
[textViews addObject: textField];
}
if([type isEqualToString: typeMultiValue]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"MultiValueCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Get value
int value = [[[NSUserDefaults standardUserDefaults] objectForKey: key] intValue];
NSArray *values = [prefSpecification objectForKey: #"Values"];
NSArray *titles = [prefSpecification objectForKey: #"Titles"];
NSString *multiValue = #"Unknown";
int index = [values indexOfObject: [NSString stringWithFormat: #"%d", value]];
if(index >= 0 && index < [values count]) {
multiValue = [titles objectAtIndex: index];
}
// Set title
cell.textLabel.text = title;
int frameSize = self.view.frame.size.width;
// Add label
UILabel *labelView = [[UILabel alloc] initWithFrame: CGRectMake((frameSize - labelCGRectWidth - 30), 12, labelCGRectWidth, labelCGRectHeight)];
labelView.textAlignment = NSTextAlignmentRight;
labelView.text = multiValue;
labelView.textColor = [UIColor grayColor];
[cell addSubview: labelView];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Track label
[labelViews addObject: labelView];
}
return cell;
}
- (void) switchChanged: (id) sender {
UISwitch* switchControl = sender;
NSString *key = [settingsKeys objectAtIndex: switchControl.tag];
NSNumber *numberValue = [NSNumber numberWithBool: switchControl.on];
[[NSUserDefaults standardUserDefaults] setObject: numberValue forKey: key];
}
- (void) textFieldChanged: (id) sender {
UITextField* textControl = sender;
NSString *key = [settingsKeys objectAtIndex: textControl.tag];
NSString *stringValue = textControl.text;
[[NSUserDefaults standardUserDefaults] setObject: stringValue forKey: key];
}
- (void) selectedMultiValue {
[self reloadTable];
}
- (void) deviceOrientationDidChange {
[self reloadTable];
}
- (void)prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
if ([[segue identifier] isEqualToString: #"changeMultiValue"])
{
MultiValueTableViewController *multiValueViewController =
[segue destinationViewController];
NSIndexPath *indexPath = [self.tableView
indexPathForSelectedRow];
// Get the dictionary item
NSDictionary *prefSpecification = [[settingsTableData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];
multiValueViewController.prefSpecification = prefSpecification;
multiValueViewController.delegate = self;
}
}
- (void) reloadTable {
for (UILabel *labelView in labelViews) {
[labelView removeFromSuperview];
}
for (UITextField *textView in textViews) {
[textView removeFromSuperview];
}
// Remove references to objects for garbage collection
labelViews = [NSMutableArray arrayWithObjects: nil];
textViews = [NSMutableArray arrayWithObjects: nil];
[self.tableView reloadData];
}
- (void) dealloc {
// Remove observers
[[NSNotificationCenter defaultCenter] removeObserver: self];
}
#end
MultiValueTableViewController.h
//
// MultiValueTableViewController.h
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
//
#import <UIKit/UIKit.h>
#import "SettingsController.h"
#protocol MultiValueDelegate
- (void) selectedMultiValue;
#end
#interface MultiValueTableViewController : UITableViewController {
NSDictionary *prefSpecification;
}
#property (nonatomic) id<MultiValueDelegate> delegate;
#property (strong, nonatomic) NSDictionary *prefSpecification;
#end
MultiValueTableViewController.m
//
// MultiValueTableViewController.m
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
//
#import "MultiValueTableViewController.h"
#interface MultiValueTableViewController ()
#end
#implementation MultiValueTableViewController
#synthesize prefSpecification;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *title = [prefSpecification objectForKey: #"Title"];
self.navigationItem.title = title;
// Avoid tab bar to overlap tableview
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *values = [prefSpecification objectForKey: #"Values"];
// Return the number of rows in the section.
return [values count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
NSString *key = [prefSpecification objectForKey: #"Key"];
NSArray *titles = [prefSpecification objectForKey: #"Titles"];
NSArray *values = [prefSpecification objectForKey: #"Values"];
NSString *title = [titles objectAtIndex: indexPath.row];
// Create cell
cell.selectionStyle = UITableViewCellSelectionStyleGray;
// Set title
cell.textLabel.text = title;
// If this is the selected value
if([[values objectAtIndex: indexPath.row] intValue] == [[[NSUserDefaults standardUserDefaults] objectForKey: key] intValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *key = [prefSpecification objectForKey: #"Key"];
NSArray *values = [prefSpecification objectForKey: #"Values"];
NSNumber *value = [values objectAtIndex: indexPath.row];
[[NSUserDefaults standardUserDefaults] setObject: value forKey: key];
[self.delegate selectedMultiValue];
[self.tableView reloadData];
}
#end
Storyboard Now go the storyboard and create a TableViewController. Select the TableViewController and Choose "Editor" -> "Embed in" -> "Navigation controller".
Set the class of the TableViewController as SettingsTableViewController. Set the identifier of the cell as "Cell", add a second TableViewCell to the TableView and set it's identifier as "MultiValueCell". Add a second TableViewController, and CTRL+CLICK and drag from the MultiValueCell to the second TableViewController. Set the class of the second TableViewController as MultiValueTableViewController. Set the identifier of the cell in the second TableViewController as "Cell" too. That's it!
#import "SettingViewController.h"
NSInteger selectedIndex;
//Use this, it works for me
- (void)viewDidLoad
{
selectedIndex = 0;
[super viewDidLoad];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50.0f;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_arrayForSaveSetting count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.row==selectedIndex) {
cell.accessoryType =UITableViewCellAccessoryCheckmark;
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
selectedIndex=indexPath.row;
[_tblSaveSetting reloadData];
}
*Update: This actually works, the style for my custom cell hasn't come across and so the cell looks blank. How then do I get the searchResultsTableView to use my custom cell?
I have implemented a search bar in my table view. Searching, filtering all work when I debug, but when I enter characters into the search bar, the results do not load or show. This is everything I'm doing:
#interface InviteTableViewController ()<UIAlertViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>
#property(nonatomic, strong) NSNumber *contactsCount;
#property(nonatomic, strong) InviteTableViewCell *selectedCell;
#property(strong, nonatomic) NSMutableArray *filteredContactArray;
#property (weak, nonatomic) IBOutlet UISearchBar *contactsSearchBar;
#end
#implementation InviteTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void) extractAllContacts: (ABAddressBookRef) addressBookRef{
NSMutableArray *contactsArray = [NSMutableArray array];
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBookRef);
for(int i = 0; i < numberOfPeople; i++){
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );
NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
if(firstName){
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
for (CFIndex i = 0; i < ABMultiValueGetCount(emails); i++) {
NSString *email = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(emails, i);
MyUser *amUser = [[MyUser alloc] init];
amUser.email =email;
NSString *fullName =[NSString stringWithFormat:#"%# %#",firstName, (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty))];
amUser.fullName = fullName;
[contactsArray addObject:amUser];
}
}
}
NSLog(#"================================count ============= %d", [contactsArray count]);
contactsArray = [contactsArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSDate *first = [(MyUser*) a fullName];
NSDate *second = [(MyUser*)b fullName];
return [first compare:second];
}];
self.inviteContactsArray = contactsArray;
self.filteredContactArray = [NSMutableArray arrayWithCapacity:[contactsArray count]];
[self.tableView reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_contactsCount = [NSNumber numberWithInt:0];
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
[self extractAllContacts: addressBookRef];
[self.tableView reloadData];
} else {
NSString *message = [NSString stringWithFormat:#"You have not given permission to use your address book. Please allow in settings "];
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Enable Contacts" message:message delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self extractAllContacts:addressBookRef];
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
}
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.filteredContactArray count];
} else {
return [self.inviteContactsArray count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"InviteCell";
InviteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if ( cell == nil ) {
cell = [[InviteTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
MyUser *person = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
person = [self.filteredContactArray objectAtIndex:[indexPath row]];
NSMutableArray *tempArray = self.filteredContactArray;
}
else
{
person = [self.inviteContactsArray objectAtIndex:[indexPath row]];
}
//InviteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
person = [self.inviteContactsArray objectAtIndex:indexPath.row];
cell.nameLabel.text = person.fullName;
cell.emailLabel.text = person.email;
return cell;
}
#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.filteredContactArray removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.fullName contains[c] %#",searchText];
self.filteredContactArray = [NSMutableArray arrayWithArray:[self.inviteContactsArray filteredArrayUsingPredicate:predicate]];
}
#pragma mark - UISearchDisplayController Delegate Methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
#end
This is what it looks like:
Agree with #rdelmar.
BUT There is a kind of tricky behavior in TableView, if you change the code in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
InviteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
....
}
to
InviteTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if added the prefix "self." your code should works fine.
and
if ( cell == nil )
{
cell = [[InviteTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
is unnecessary. it will just create a standard "Subtitle Cell" for you that seems not you want, just remove them.
If you want to use the same cell for your main table and the search results table, you should make the cell in a xib (or you could do it entirely in code). In viewDidLoad, register the nib for both table views,
- (void)viewDidLoad {
[super viewDidLoad];
[self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:#"InviteTableViewCell" bundle:nil] forCellReuseIdentifier:#"InviteCell"];
[self.tableView registerNib:[UINib nibWithNibName:#"InviteTableViewCell" bundle:nil] forCellReuseIdentifier:#"inviteCell"];
}
In cellForRowAtIndexPath, you can do something like this,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
InviteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"InviteCell" forIndexPath:indexPath];
MyUser *person = ([tableView isEqual:self.tableView])? self.inviteContactsArray[indexPath.row] : self.filteredContactArray[indexPath row];
cell.nameLabel.text = person.fullName;
cell.emailLabel.text = person.email;
return cell;
}
If you've already setup your cell in the storyboard, you can copy and paste it into an empty xib file, so you don't have to set it up all over again. You can delete the cell from your storyboard table view since you will be getting the cell from the xib file instead.
Below line in viewdidload should do the trick
self.searchDisplayController.searchResultsTableView.rowHeight = self.tableView.rowHeight
I am having an app in which I am showing some data in my tableview as shown in the ScreenShot1 below.
Screenshot1.
Now I am having an imageview On my each tableview cell.
If I select any of the cell from tableview, I want an image on that selected cell as shown in screenshot2 below.
Screenshot2.
Now If I select any other cell form tableview. I want to set my imageview's image only on those selected cells as shown in Screenshot3 below.
Below is the code I am doing.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
lblRecipe = [[UILabel alloc] initWithFrame:CGRectMake(50, 5, 600, 32)];
lblRecipe.backgroundColor = [UIColor clearColor];
lblRecipe.font = [UIFont systemFontOfSize:20.0f];
[cell.contentView addSubview:lblRecipe];
checkedimg=[[UIImageView alloc]initWithFrame:CGRectMake(40, 20, 500, 5)];
[cell.contentView addSubview:checkedimg];
lblRecipe.text = [array objectAtIndex:indexPath.row];
return cell;
}
On Tableview's didselect method, what should i keep to have this kind of a functionality?
I searched a lot and there are lots of questions for this but couldn't find one for this.
So Please help me on this.
Thanks in advance.
You may be better off just subclassing UITableViewCell, as this would let you change the way the cells draw, and add a property or something of the sort to indicate this state.
To create the subclass, create a new file in Xcode, and select UITableViewCell as its superclass.
Then, within that subclass, you could implement a method called setStrikeThrough: like this that you can call on the cell you dequeue from the table:
- (void) setStrikeThrough:(BOOL) striked {
if(striked) {
// create image view here and display, if it doesn't exist
} else {
// hide image view
}
}
Then, in the view controller that holds the table, you'd call this in viewDidLoad to register your subclass with the table view:
[_tableView registerClass:[YourAmazingTableCell class] forCellReuseIdentifier:#"AmazingCellSubclass"];
You would then alter your tableView:cellForRowAtIndexPath: to be something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"AmazingCellSubclass";
YourAmazingTableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[YourAmazingTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// here you would look up if this row should be selected
[cell setStrikeThrough:YES];
// Set up the remainder of the cell, again based on the row
cell.textLabel.text = #"chilli";
return cell;
}
Try this,
Add a NSMutableArray checkedCellArray, as property and alloc init.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *labelRecipe = [[UILabel alloc] initWithFrame:CGRectMake(50, 5, 600, 32)];
labelRecipe.backgroundColor = [UIColor clearColor];
labelRecipe.font = [UIFont systemFontOfSize:20.0f];
labelRecipe.tag = kRecipeTag;//a tag to label
[cell.contentView addSubview:labelRecipe];
UIImageView *checkedimg = [[UIImageView alloc]initWithFrame:CGRectMake(40, 20, 500, 5)];
[checkedimg setImage:[UIImage imageNamed:#"checked.png"];
checkedimg.tag = kCheckedImageTag;//a tag to imageView
[cell.contentView addSubview:checkedimg];
checkedimg.hidden = YES;
}
UILabel *labelRecipe = (UILabel *)[cell.contentView viewWithTag:kRecipeTag];
UIImageView *checkedimg = (UIImageView *)[cell.contentView viewWithTag:kCheckedImageTag];
if ([self.checkedCellArray containsObject:indexPath]) {
checkedimg.hidden = NO;
} else {
checkedimg.hidden = YES;
}
labelRecipe.text = [array objectAtIndex:indexPath.row];
return cell;
}
and in didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (![self.checkedCellArray containsObject:indexPath]) {
[self.checkedCellArray addObject:indexPath];
}
NSArray* rowsToReload = [NSArray arrayWithObjects:indexPath, nil];
[UITableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationNone];
}
Oky hear you can do like this
just subclass the tableview cell
in CustomCell.h file
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
#property (nonatomic,retain)UIImageView *dashImageView; //add a property of image view
#end
in CustomCell.m file
#import "CustomCell.h"
#implementation CustomCell
{
BOOL isCellSelected; //add this to check
}
#synthesize dashImageView = _dashImageView; //synthesize it
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
isCellSelected = NO;//initially set it no
_dashImageView = [[UIImageView alloc]initWithFrame:CGRectMake(self.bounds.origin.x + 15, self.bounds.origin.y + 10, self.bounds.size.width, 15.0f)];
_dashImageView.backgroundColor = [UIColor redColor];
_dashImageView.tag = 12345;
_dashImageView.hidden = YES;
[self.contentView addSubview:_dashImageView];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
UIImageView *dashImageVIew = (UIImageView *)[self viewWithTag:12345];
if(dashImageVIew)
{
if(!isCellSelected)
{
dashImageVIew.hidden = NO;
isCellSelected = YES;
}
else
{
dashImageVIew.hidden = YES;
isCellSelected = NO;
}
}
}
}
EDIT:2 *For selecting both section and row for the table view*
there is no change in the custom cell only change in the controller so that u hav to reflect what changes that u made to tableview cell i.e weather u select or deselect
since u want select section along with the selected row.(your requirement) there are various way to do that, but i go by using some model object which contains index section and row.
for that u need to create a subclass of NSObject and name it as MySelectedIndex then in
in MySelectedIndex.h file
#import <Foundation/Foundation.h>
#interface MySelectedIndex : NSObject<NSCoding>//to store and retrieve data like user defaults, for non-property list objects, confirms to NSCoding protocol
#property (nonatomic,retain) NSNumber *selectedRow;
#property (nonatomic,retain) NSNumber *selectedSection;
- (id)initWithSelectedRow:(NSNumber *)selRow andSelectedIndex:(NSNumber *)selSection;
#end
in MySelectedIndex.m file
#import "MySelectedIndex.h"
#implementation MySelectedIndex
#synthesize selectedSection = _selectedSection;
#synthesize selectedRow = _selectedRow;
- (id)initWithSelectedRow:(NSNumber *)selRow andSelectedIndex:(NSNumber *)selSection
{
self = [super init];
if(self)
{
_selectedRow = selRow;
_selectedSection = selSection;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_selectedRow forKey:#"ROW"];
[aCoder encodeObject:_selectedSection forKey:#"SECTION"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if(self)
{
_selectedRow = [aDecoder decodeObjectForKey:#"ROW"];
_selectedSection = [aDecoder decodeObjectForKey:#"SECTION"];
}
return self;
}
#end
now get back to controller where u are using the table
in .m file
import it #import "CustomCell.h"
and in the method
in viewController.m
#interface SampleViewController ()<UITableViewDataSource,UITableViewDelegate>
{
NSMutableArray *indexes;//to store in defaults
NSMutableArray *indexesRed;//to red from the defaults
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//...other stuff's
indexes = [[NSMutableArray alloc]init];//initilise your arrays
indexesRed = [[NSMutableArray alloc]init];
//after initilizing your array
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"SELECTED_CELLL"];
if(data != nil)
{
indexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[indexes retain];//you hav to retain the objects other wise u will get crash, make sure u will release it by proper memory mabagement (if u are not using ARC)
}
}
as u said u are using buttons to pop this viewcontroller so already u know where to place so put this in all the action methods(just replace where u are saving to user defaults )
and also read the comments that i put in the code
//put all these codes where u poping this viewcontroller
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:indexes];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"SELECTED_CELLL"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.navigationController popViewControllerAnimated:YES];
replace the cellForRowAtIndexPath by following code (i added in the below method)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
CustomTabedCell *Cell = [tableView dequeueReusableCellWithIdentifier:#"CELL"];
if(Cell == nil)
{
Cell = [[CustomTabedCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CELL"];
}
Cell.textLabel.text = #"Apple";
NSNumber *rowNum = [NSNumber numberWithInt:indexPath.row];
NSNumber *secNum = [NSNumber numberWithInt:indexPath.section];
__block BOOL isImageHidden = YES;
[indexes enumerateObjectsUsingBlock:^(MySelectedIndex *obj, NSUInteger idx, BOOL *stop)
{
if(obj.selectedRow == rowNum && obj.selectedSection == secNum)
{
isImageHidden = NO;
}
}];
Cell.dashImageView.hidden = isImageHidden;
[Cell.contentView bringSubviewToFront:Cell.dashImageView];//as i notised the red dash imageview appers below the text, but in your image it is appearing above the text so put this line to bring the imageview above the text if dont want commnent this
return Cell;
}
finally in the method didSelectRowAtIndexPath replace by following method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *selSection = [NSNumber numberWithInt:indexPath.section];
NSNumber *selRow = [NSNumber numberWithInt:indexPath.row];
if([indexes count] == 0)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else if ([indexes count] == 1)
{
MySelectedIndex *singleIndex = [indexes objectAtIndex:0];
if(singleIndex.selectedSection == selSection & singleIndex.selectedRow == selRow)
{
[indexes removeAllObjects];
}
else
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
}
else
{
__block BOOL addSelectedRow = NO;
__block int index = -1;
[indexes enumerateObjectsUsingBlock:^(MySelectedIndex *obj, NSUInteger idx, BOOL *stop) {
if(!((obj.selectedRow == selRow) & (obj.selectedSection == selSection)))
{
addSelectedRow = YES;
}
else
{
index = idx;
addSelectedRow = NO;
*stop = YES;
}
}];
if(addSelectedRow)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else
{
if(index >= 0)
{
[indexes removeObjectAtIndex:index];
}
}
}
NSLog(#"%#",indexes.description);
[tableView reloadData];
}
edit for not able to select more than 13 row's
in viewDidLoad
do like this
indexes = [[NSMutableArray alloc]init];
indexesRed = [[NSMutableArray alloc]init];
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"SELECTED_CELLL"];
if(data != nil)
{
indexes = [[NSKeyedUnarchiver unarchiveObjectWithData:data] retain]; //hear only u retain it
NSLog(#"%#",indexes.description);//check the description, how many objects are there in the array
NSLog(#"indexes->%d",[indexes count]);
}
//[indexes retain];//comment this
} //end of `viewDidLoad`
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabedCell *Cell = [tableView dequeueReusableCellWithIdentifier:#"CELL"];
if(Cell == nil)
{
Cell = [[CustomTabedCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CELL"];
}
Cell.textLabel.text = #"Apple";
__block BOOL isImageHidden = YES; //remove that __block qualifier
// __block NSInteger currentRow = indexPath.row;
// __block NSInteger currentSection = indexPath.section;
// [self.indexes enumerateObjectsUsingBlock:^(MySelectedIndex *obj, NSUInteger idx, BOOL *stop)
// {
// NSInteger rowNum = [obj.selectedRow integerValue];
// NSInteger secNum = [obj.selectedSection integerValue];
// if(rowNum == currentRow && secNum == currentSection)
// {
// isImageHidden = NO;
// }
// }];
//comment block code and use simple for loop, looping through each obj slightly faster
for(int k = 0;k < [indexes count];k++)
{
MySelectedIndex *seletedIndex = [indexes objectAtIndex:k];
NSInteger row = [seletedIndex.selectedRow integerValue];
NSInteger sec = [seletedIndex.selectedSection integerValue];
if(row == indexPath.row & sec == indexPath.section)
{
isImageHidden = NO;
}
}
Cell.dashImageView.hidden = isImageHidden;
[Cell.contentView bringSubviewToFront:Cell.dashImageView];//as i notised the red dash imageview appers below the text, but in your image it is appearing above the text so put this line to bring the imageview above the text if dont want commnent this
return Cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *selSection = [NSNumber numberWithInt:indexPath.section];
NSNumber *selRow = [NSNumber numberWithInt:indexPath.row];
if([indexes count] == 0)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else if ([indexes count] == 1)
{
MySelectedIndex *singleIndex = [indexes objectAtIndex:0];
NSInteger rowNumInInt = [singleIndex.selectedRow integerValue];
NSInteger sectionInInt = [singleIndex.selectedSection integerValue];
if(sectionInInt == indexPath.section & rowNumInInt == indexPath.row)
{
[indexes removeAllObjects];
}
else
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
}
else
{
BOOL addSelectedRow = NO;
int index = -1;
for(int j = 0; j < [indexes count];j++)
{
MySelectedIndex *selectedObj = [indexes objectAtIndex:j];
NSInteger rowInt = [selectedObj.selectedRow integerValue];
NSInteger sectionInt = [selectedObj.selectedSection integerValue];
if(!(rowInt == indexPath.row && sectionInt == indexPath.section))
{
addSelectedRow = YES;
}
else
{
index = j;
addSelectedRow = NO;
// *stop = YES;
}
}
if(addSelectedRow)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else
{
if(index >= 0)
{
[indexes removeObjectAtIndex:index];
}
}
}
// [tableView reloadData];
NSLog(#"indexes count->%d",[indexes count]);//check each time by selecting and deselectin weateher it is working or not
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
thats it simple .. :)
hope this helps u .. :)
Try This it work good
#import "ViewController.h"
#interface ViewController ()<UITableViewDataSource,UITableViewDelegate>
{
UITableView *menuTableView;
NSMutableArray *colorsArray,*tagvalueArray;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
menuTableView=[[UITableView alloc]initWithFrame:CGRectMake(0, 60, 320, 404) style:UITableViewStyleGrouped];
menuTableView.delegate=self;
menuTableView.dataSource=self;
menuTableView.separatorColor=[UIColor grayColor];
[self.view addSubview:menuTableView];
NSArray *serverResponseArray=[[NSArray alloc]initWithObjects:#"red",#"yellow",#"pink",#"none", nil]; // consider this array as the information you receive from DB or server
colorsArray =[[NSMutableArray alloc]init];
tagvalueArray =[[NSMutableArray alloc]init];
for (int i=0; i<serverResponseArray.count; i++) {
[colorsArray addObject:serverResponseArray[i]];
[tagvalueArray addObject:#"0"];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return colorsArray.count;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
for (UIView *view in cell.contentView.subviews) {
[view removeFromSuperview];
}
cell.textLabel.text =colorsArray[indexPath.row];
if ([[tagvalueArray objectAtIndex:indexPath.row]intValue]==1) {
UIImageView *checkedimg=[[UIImageView alloc]initWithFrame:CGRectMake(40, 20, 500, 5)];
checkedimg.backgroundColor=[UIColor redColor];
[cell.contentView addSubview:checkedimg];
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#",colorsArray[indexPath.row]);
[tagvalueArray replaceObjectAtIndex:indexPath.row withObject:#"1"];
[menuTableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end