I am trying to create a UITableView with two different sections. I know I can group them on an attribute of my managed object. For instance if I'd like to group them per name I'd do:
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context
sectionNameKeyPath:#"name"
cacheName:#"uploadProperties"];
And I return the number of secionts like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[_fetchedResultsController sections] count];
}
The problem though is that I do not want to group it per attribute (such as the name). I want to group them for specific values, namely one part that has pud_id = 0 and another section that has pud_id > 0.
How can I achieve this? Is it possible to use kind of a where clause? Or can I create a property on my managed object and use this in the sectionNameKeyPath such as:
- (BOOL) hasPudZero {
if (self.pud_id == 0)
return YES;
return NO;
}
??
Thanks for your input!
Perhaps you could create a transient attribute of type NSString that returns one of two strings?
- (NSString *)sectionIdentifier
{
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveValueForKey:#"sectionIdentifier"];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp) {
if (self.pud_id == 0) {
tmp = #"SectionOne";
} else {
tmp = #"SectionTwo";
}
[self setPrimitiveValue:tmp forKey:#"sectionIdentifier"];
}
return tmp;
}
and then use sectionNameKeyPath:#"sectionIdentifier" when creating your NSFetchedResultsController. Make sure you set the primitive value of sectionIdentifier back to nil if the value of put_id changes.
Related
I have an array of NSDictionary. Each array item has a key named "Name". Now I want to remove duplicate entries based on this name value.
This work perfectly:
aMyArray = [aMyArray valueForKeyPath:#"#distinctUnionOfObjects.Name"];
The problem are that result array still contains duplicates string, based on the case. Ex: [#"Franck", "franck"]
How can I remove these duplicate?
Thanks
You could try to do this
// in your class implementation
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[self class]]) {
return NO;
}
typeof(self) obj = (typeof(self))object;
return ([self.Name caseInsensitiveCompare:obj.Name] == NSOrderedSame);
}
- (NSUInteger)hash
{
return [[self.Name lowercaseString] hash];
}
// and then
NSSet *distinctObjects = [[NSSet alloc] initWithArray:array];
NSArray *result = distinctObjects.allObjects;
Alternatively you could customise this KVC collection operator by swizzling valueForKeyPath: to parse your custom DSL, possibly winding up with something like
aMyArray = [aMyArray valueForKeyPath:#"#distinctUnionOfObjects[caseInsensitive].Name"];
which doesn't seem to be a good idea for me, but it certainly a viable solution to your problem.
I have NSTextField with placeholder. And it's binded to some integer property. So I want to display empty text in the field (with placeholder shown) when binded integer is zero.
Is it possible to do it?
(Update)
I discovered that this can be done through NSNumberFormatter - it has —(void) setZeroSymbol: (NSString*) string method. Not tried yet this in practice...
You could use an NSValueTransformer.
(Just in case)Create a new class, subclass from NSValueTransformer. In the implementation, add something like this:
+(Class)transformedValueClass {
return [NSString class];
}
-(id)transformedValue:(id)value {
if (value == nil) {
return nil;
} else {
if ([value integerValue] == 0) {
return #"";
} else {
return [NSString stringWithFormat:#"%d", [value stringValue]];
}
}
}
In Interface Builder, select your field, go to the bindings tab, and in the Value Transformer drop down, either select or type in your class name you made. This should prevent you from having to worry about modifying it elsewhere. I'm not 100% positive about it showing the placeholder (I don't have a Mac available right now).
EDIT:
I can confirm that this does indeed work. Here is a link to a github project I made to show how to use it: https://github.com/macandyp/ZeroTransformer
check the integer value before binding, if you are binding at runtime. Try
int i;
if (i == 0)
{
txt.placeholder = #"text";
}
else
{
[txt setStringValue:[NSString stringWithFormat:#"%d",i]];
}
You can not do conditional binding.
You need to create another property that will hold the value based on condition and use that property and bind to textfield.
I am using bindedString and bindedInteger. bindedString is bound to text field.
Whenever some action is performed it is updated.
- (id)init{
self = [super init];
if (self) {
self.bindedString=#"place holder string";
}
return self;
}
- (IBAction)button:(id)sender {
if (self.bindedInteger==0) {
self.bindedString=#"place holder string";
}
else{
self.bindedString=[NSString stringWithFormat:#"%ld",self.bindedInteger];
}
}
This example is contrived, but it shows my point.
So, if I have an object graph like the following:
{
sex = male;
uid = 637650940;
work = ({
employer = {
id = 116420715044499;
name = "Software Engineer";
};
"end_date" = "0000-00";
"start_date" = "0000-00";
}, {
employer = {
id = 188733137832278;
name = "Apple";
};
});
},
//Some more objects
(This is an NSArray containing NSDictionarys that have an object of type NSArray).
The key field is work. I want a Key Path that will take the first object in the work array.
If I do this:
NSArray* work = [outerArrayObject objectForKey: #"work"];
id name = [work valueForKeyPath: #"employer.name"];
I get an array containing each name (In the above case, Software Engineer & Apple). Is there a collection operator or something to return the first object? Bonus points if you can develop a Key Path to sort each work by start_date also :)
#PauldeLange - Your answer and links were helpful.
The following simpler version works too (at least as of Xcode 6)
id name = [work valueForKeyPath: #"employer.name.#firstObject”];
In the above 'firstObject' refers to the predefined method on NSArray. If the second object is needed, you can define the following:
#implementation NSArray (CustomKVOOperators)
- (id) secondObject {
return [self count] >=2 ? self[1] : nil;
}
#end
And use:
id name = [work valueForKeyPath: #"employer.name.#secondObject”];
Well to answer my own question, one way to do it is this:
1) Define the following category
#implementation NSArray (CustomKVOOperators)
- (id) _firstForKeyPath: (NSString*) keyPath {
NSArray* array = [self valueForKeyPath: keyPath];
if( [array respondsToSelector: #selector(objectAtIndex:)] &&
[array respondsToSelector: #selector(count)]) {
if( [array count] )
return [array objectAtIndex: 0];
else
return nil;
}
else {
return nil;
}
}
#end
2) Use this KeyPath syntax
NSArray* work = [outerArrayObject objectForKey: #"work"];
id name = [work valueForKeyPath: #"#first.employer.name"];
Thanks to this clever person.
I have NSTableView with two columns,one with Checkbox and another with NSString.
i want to define all the Checkbox to true
i try to do it with:
if (tableColumn == first) {
return YES;
}else if (tableColumn == second) {
NSString *country = [array objectAtIndex:row];
return [country lastPathComponent];
}
but it give me crash when i start the app.
It looks like your code is executing in tableView:objectValueForTableColumn:row:. In that case, the table view only accepts objects, not primitive values.
Try changing return YES to return [NSNumber numberWithBool:YES].
I am trying to create a generic UITableView in my iPhone app.
I have a UITableView which populates the data using an array via a SELECT query loop.
I add the data into my array and populate the array in cellForRowAtIndexPath:.
I get the section header using that array and by using a sort method, I put the section headers in Array1.
I would like to have titleForHeaderInSection: work by having section 0 be a static header name and sections 1 and later become generic, meaning the header name will come from Array1.
I am not sure how can I create that logic since the app always throws EXC_BAD_ACCESS with the code below.
My logic: I keep the count of the array in an int and see if the value is greater than 0. If it is, I add the section header for and objectAtIndex:0, otherwise I use the static one. But when the count gets to 2, for section 2 and objectAtIndex:1, it breaks and throws EXC_BAD_ACCESS.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
int value = [[self Array1] count];
if(section == 0)
return #"Countries";
if (value > 0) {
if (section == value){
return [[self Array1] objectAtIndex:section - 1];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
int count = [[self Array1] count];
return count + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int value = [[self Array1] count];
if (section == 0) {
return [self.Array count];
}
if (value > 0) {
if (section == [[self Array1] count]) {
NSString *initialLetter = [[self Array1] objectAtIndex:section - 1];
// get the array of elements that begin with that letter
NSArray *elementsWithInitialLetter = [self elementsWithInitialLetter:initialLetter];
// return the count
return [elementsWithInitialLetter count];
}
}
}
Looks like you're just missing a retain on the iVar backing the Array1 method. declare the array as a property:
#property (nonatomic, retain) NSArray* datastore;
Then cache the value you're referring to in the Array1 method in this method (probably in viewDidLoad).
self.datastore = [self Array1];
Then replace all remaining references to [self Array1] with self.datastore. Build run and see if it still crashes. (Don't forget to set self.datastore = nil in your dealloc!