search through data in multiple arrays - objective-c

im doing a searchbar for my UITableview.
So far its only filtering one array in - (void) searchthroughdata, namely the self.Title.
But I want it to filter through two arrays - the self.Title and self.Description.
My .h file:
#property (nonatomic, strong) NSArray * Images;
#property (nonatomic, strong) NSArray * Title;
#property (nonatomic, strong) NSArray * Description;
#property (nonatomic, strong) NSMutableArray *results;
#property (nonatomic, strong) IBOutlet UISearchBar *SearchBar;
My .m file:
-(void)searchThroughData {
self.results = nil;
NSPredicate *resultsPredicate = [NSPredicate predicateWithFormat:#"SELF contains [search] %#", self.SearchBar.text];
self.results = [[self.Title filteredArrayUsingPredicate:resultsPredicate] mutableCopy];
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self searchThroughData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.tableView) {
return _Title.count;
} else {
[self searchThroughData];
return self.results.count;
}
// Return the number of rows in the section.
//return _Title.count;
}
How do I make it filter through the NSArray * Description as well?

Your best option would be to not have multiple arrays. Instead, create a custom object with your
#property (nonatomic, strong) NSArray * Images;
#property (nonatomic, strong) NSArray * Title;
#property (nonatomic, strong) NSArray * Description;
(or a dictionary) and have a single array containing those objects (I am assuming something about your data model here...).
Now, when you filter you can just check each item in the predicate with an OR.

Related

Which method is better / cleaner?

While looping the
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {}
method im setting data to my custom cell (an image and a text). I was wondering what's the best way to achieve this.
I can use 2 different array (image array and text array) and do something like:
cell.image setImage: //get image from imageArray with indexPath.row
cell.text.text = [textArray objectAtIndex:indexPath.row];
or use a multi dimensional array like this:
cell.image setImage: [imageArray objectAtIndex:indexPath.row] objectAtIndex:0;
cell.text.text = [textArray objectAtIndex:indexPath.row] objectAtIndex:1;
// or use of dictionary with keys
What method is quicker or more readable?
Personally I think the following is the cleanest solution:
Create a model for the items in your array.
Create an UITableViewCell subclass to display the model in the cell. The subclass will have a property that accepts the model and redraw itself when the model changes.
Let's say we have a news app. The array is filled with items for which we create the model NewsItem. The model header could look like this:
NewsItem.h
#interface FSNewsItem : NSObject <NSCoding>
#property (nonatomic, copy, readonly) NSString *title;
#property (nonatomic, copy, readonly) NSURL *URL;
#property (nonatomic, copy, readonly) NSString *time;
#property (nonatomic, copy, readonly) NSString *points;
#property (nonatomic, copy, readonly) NSString *author;
// initialiser
+ (FSNewsItem *)newsItemWithTitleNode:(HTMLNode *)node
subTextNode:(HTMLNode *)subNode;
#end
Now for the cell we create a NewsItemCell. The code for NewsItemCell might look like the following:
NewsItemCell.h
#interface FSNewsItemCell : UITableViewCell
#property (nonatomic, strong) FSNewsItem *newsItem;
#end
NewsItemCell.m
#interface FSNewsCell ()
#property (nonatomic, strong) UILabel *titleLabel;
#property (nonatomic, strong) UILabel *detailLabel;
#end
#implementation FSNewsItemCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
self.titleLabel = [[UILabel alloc] init];
[self addSubview:_titleLabel];
self.detailLabel = [[UILabel alloc] init];
[self addSubview:_detailLabel];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
const CGRect bounds = self.bounds;
CGFloat width = bounds.size.width - (FS_FLOAT_PADDING * 2) - 15.0f;
_titleLabel.frame = CGRectMake(FS_FLOAT_PADDING, FS_FLOAT_CELL_PADDING_VERTICAL, width, 20.0f);
CGFloat y = _titleLabel.frame.size.height + (FS_FLOAT_CELL_PADDING_VERTICAL * 2);
_detailLabel.frame = CGRectMake(FS_FLOAT_PADDING, y, width, 15.0f);
}
#pragma mark - Private
- (void)setNewsItem:(FSNewsItem *)newsItem
{
if (_newsItem == newsItem)
{
return;
}
_newsItem = newsItem;
self.titleLabel.text = newsItem.title;
if (_newsItem.points && _newsItem.time)
{
self.detailLabel.text = [NSString stringWithFormat:#"%# | %#", _newsItem.points, newsItem.time];
}
else
{
self.detailLabel.text = newsItem.time;
}
[self setNeedsLayout];
}
Finally, when we want to display the news item in the cell, our code would look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
FSNewsItemCell *cell = (FSNewsItemCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[FSNewsItemCell alloc] initWithStyle:UITableViewCellStylePlain reuseIdentifier:CellIdentifier];
}
cell.newsItem = [_items objectAtIndex:indexPath.row];
return cell;
}
This is in my opinion the cleanest solution and the approach I take most of the time. I like to keep my view controllers small. I also like the fact that my view controller doesn't have to know which controls my (custom) table view cell has. The table view cell gets full responsibility on how to draw itself depending on the data supplied.
I would add a simple data class and put class instances in the NSArray. Or use a NSArray of NSDictionary objects so the items can be addressed by name, not position.
Example class code (classes are very easy these days):
#interface DisplayItems : NSObject
#property (nonatomic, strong) NSString *text;
#property (nonatomic, strong) UIImage *image;
#end
#implementation DisplayItems
#end
One idea is to create a custom class:
#interface CellInfo : NSObject
#property (....) UIImage *image;
#property (....) NSString *text;
#end
Then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
...
CellInfo *info = [self.cellInfoArray objectAtIndex:indexPath.row];
cell.image = info.image;
cell.text = info.text;
...
}

ObjC get property name

I've looking for a way to get a property name as StringValue from inside a method.
Lets say:
My class has X Subviews from the Type UILabel.
#property (strong, nonatomic) UILabel *firstLabel;
#property (strong, nonatomic) UILabel *secondLabel;
[...]
and so on.
Inside the method foo, the views are iterated as followed:
-(void) foo
{
for (UIView *view in self.subviews) {
if( [view isKindOfClass:[UILabel class]] ) {
/*
codeblock that gets the property name.
*/
}
}
}
The Result should be something like that:
THE propertyName(NSString) OF view(UILabel) IS "firstLabel"
I've tried class_getInstanceVariable, object_getIvar and property_getName without Success.
For example, the code for:
[...]
property_getName((void*)&view)
[...]
Returns:
<UILabel: 0x6b768c0; frame = (65 375; 219 21); text = 'Something'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x6b76930>>
But i'm looking for this kind of result: "firstLabel" , "secondLabel" and so on.
Solved
As in the Reply of graver described the solution is:
class_copyIvarList which returns the name of the Ivars.
Ivar* ivars = class_copyIvarList(clazz, &count);
NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
const char* ivarName = ivar_getName(ivars[i]);
[ivarArray addObject:[NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
}
free(ivars);
See posts:
https://stackoverflow.com/a/2302808/1228534
and
Objective C Introspection/Reflection
untested code from Getting an array of properties for an object in Objective-C
id currentClass = [self class];
NSString *propertyName;
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(currentClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
propertyName = [NSString stringWithCString:property_getName(property)];
NSLog#("The propertyName is %#",propertyName);
}
Easy way to get a specific property name without running a loop
Lets say custom object is like below
#interface StoreLocation : NSObject
#property (nonatomic, strong) NSString *city;
#property (nonatomic, strong) NSNumber *lat;
#property (nonatomic, strong) NSNumber *lon;
#property (nonatomic, strong) NSString *street;
#property (nonatomic, strong) NSString *state;
#property (nonatomic, strong) NSString *code;
#end
#interface AppleStore : NSObject
#property (nonatomic, strong) StoreLocation *storeLocation;
#end
Below Objective macros will get the desired result
#define propertyKeyPath(property) (#""#property)
#define propertyKeyPathLastComponent(property) [[(#""#property)componentsSeparatedByString:#"."] lastObject]
Use the code below to get property name
NSLog(#"%#", propertyKeyPath(appleStore.storeLocation)); //appleStore.storeLocation
NSLog(#"%#", propertyKeyPath(appleStore.storeLocation.street)); //appleStore.storeLocation.street
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation)); //storeLocation
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //street
Source : http://www.g8production.com/post/78429904103/get-property-name-as-string-without-using-the-runtime

Objective C serialize a collection to JSON/XML

I have a classes looking like this:
#interface AISlideItem: NSObject <NSCoding>
{
NSString* PlaceHolderName;
NSInteger PlaceHolderID;
}
#property (nonatomic, strong) NSString* PlaceHolderName;
#property (nonatomic) NSInteger PlaceHolderID;
#end
#interface AITitleSlideItem : AISlideItem
{
NSString* Title;
}
#property (nonatomic, strong) NSString* Title;
#end
#interface AIParagraphSlideItem : AISlideItem
{
NSMutableArray* Paragraphs;
}
#property (nonatomic, strong) NSMutableArray* Paragraphs;
#end
#interface AITextSlideItem : AISlideItem
{
NSString* Text;
}
#property (nonatomic, strong) NSString* Text;
#end
#interface AIPictureSlideItem : AISlideItem
{
NSMutableData* Picture;
}
#property (nonatomic, strong) NSMutableData* Picture;
#end
#interface AISlideContent : NSObject
{
NSString* LayoutName;
NSMutableArray* Items;
}
#property (nonatomic,strong) NSString* LayoutName;
#property (nonatomic,strong) NSMutableArray* Items;
#end
#interface ContentParaghraph : NSObject
{
NSInteger Level;
NSString* Text;
}
#property (nonatomic) NSInteger Level;
#property (nonatomic, strong) NSString* Text;
#end
I create a complex collection from these classes like this:
NSMutableArray* l = [[NSMutableArray alloc]init ];
AITitleSlideItem* tis1 = [[AITitleSlideItem alloc]init];
[tis1 setPlaceHolderName:#"I don't think we're using this..."];
[tis1 setPlaceHolderID:1];
[tis1 setTitle:#"This is a title"];
AITextSlideItem* tes1 = [[AITextSlideItem alloc]init];
[tes1 setPlaceHolderName:#"I don't think we're using this..."];
[tes1 setPlaceHolderID:2];
[tes1 setText:#"This is the description"];
AISlideContent* slide1 = [[AISlideContent alloc]init];
[slide1 setLayoutName:#"Title content"];
[[slide1 Items]addObject:tis1];
[[slide1 Items]addObject:tes1];
[l addObject:slide1];
[l addObject:slide1];
What i want to to serialize the NSMutableArray l into any portable format like json or xml.
Could you please post some code doing so?
Sincerely
Zoli
After further investigation i found nothing more suitable for my needs.
I decided to learn a bit more about JSON and create a function that creates the JSON structure manually. Even if it took 2-3 hours, it did worth all the effort.

Error: variable is not a CFArray

I am trying to create custom UITableViewCells, following this article. I add outlets for three fields in the cell. I am taking data stored in an NSMutableArray and placing them in the custom cell labels. When debugging, I can see the data in the source array elements, but this is in the cell labels: "variable is not a CFArray". Obviously it's not working... here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"--> cellForRowAtIndexPath");
static NSString *CellIdentifier = #"siteTableCell";
SiteListingsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[SiteListingsCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.staLabel.text = [self.staLabels objectAtIndex: [indexPath row]];
cell.descLabel.text = [self.descLabels objectAtIndex:[indexPath row]];
cell.dateLabel.text = [self.dateLabels objectAtIndex:[indexPath row]];
return cell;
}
This is the definition of the labels:
#interface SiteListingsCell : UITableViewCell {
}
#property (nonatomic, strong) IBOutlet UILabel *staLabel;
#property (nonatomic, strong) IBOutlet UILabel *descLabel;
#property (nonatomic, strong) IBOutlet UILabel *dateLabel;
#end
This is the definition of sArray:
#interface sArray : NSObject { // class (struct) to hold site information
}
#property (nonatomic, readwrite) NSString *sSiteID;
#property (nonatomic, readwrite) NSString *sInitialSTA;
#property (nonatomic, readwrite) NSString *sElev;
#property (nonatomic, readwrite) NSString *sJobDesc;
#property (nonatomic, readwrite) NSString *sJobDate;
#end
UPDATE: This code gets called when I want to fill sArray and move the values to the custom cell labels:
- (void) displaySites {
NSLog(#"displaySites");
// get list of sites and place them in sArray
slSQLite *dbCode = [[slSQLite alloc] init];
[dbCode getListOfSites];
// put site data into array for display
NSLog(#"\n2-The array listOfSites contains %d items", dbCode.listOfSites.count);
// sArray *sa = [[sArray alloc] init]; // initialize sArray object
for(int i = 0; i <dbCode.listOfSites.count; i++) {
sArray *sa = [dbCode.listOfSites objectAtIndex:i]; // get the sArray out of listOfSites
staLabels = sa.sSiteID;
descLabels = sa.sJobDesc;
dateLabels = sa.sJobDate;
}
return;
}
How do I fix this so it works?
Try to do:
staLabels.text = sa.sSiteID;
ect...

Filled NSArray unintentionally loses its content

An array is filled properly but when I try to access to content again, seems that is empty again!! trying to post as many code as necessary. Thanks for help.
I declare appData in .h file:
#interface userViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *appData;
IBOutlet UIActivityIndicatorView *activityIndicator;
IBOutlet UILabel *status;
IBOutlet UITableView *mainTable;
}
#property (nonatomic, retain) NSArray *appData;
#property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
#property (nonatomic, retain) UILabel *status;
#property (nonatomic, retain) UITableView *mainTable;
- (IBAction) refresca: (id)sender;
- (void)getData: (NSString *)usuari: (NSString *)clau;
#end
in .m file is synthesized and released. appData is properly filled when connection request ends and then, when I reload tableview, when numberOfRowsInSection executed (while loop also in order to test), appData is empty!
- (void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"%#", [request responseString]);
self.appData = [[request responseString] componentsSeparatedByString: #"#"];
int x =0;
while (x<[appData count] - 1)
{
NSLog(#"Aplicaciones = %#",[appData objectAtIndex: x]);
x = x+1;
}
[activityIndicator stopAnimating];
activityIndicator.hidden = YES;
status.hidden = YES;
mainTable.hidden = NO;
[mainTable reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([appData count] != 0)
{
NSLog(#"Counter = %#",[appData objectAtIndex:0]);
}
return [appData count]-1;
}
A couple of things here.
Your test logging in numberOfRows will cause the app to hang if appdata ever has a non-zero count.
Are you sure you are getting the the same appData object that you populate in requestFinished ?
I suggest using the accessor in numberOfRows as in [self.appData count] which might sort out the problem.
And is there a specific reason you subtract one from the count? As you will lose one element from the array in the tableView that way