I am creating a searchbar in a tableview but I have problems with this method delegate because I fill the table view with array inside another array...I show my code:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
ProgramAppDelegate *appDelegate = (ProgramAppDelegate *)[[UIApplication sharedApplication] delegate];
[tableData removeAllObjects];// remove all data that belongs to previous search
if([searchText isEqualToString:#""] || searchText==nil){
[myTableView reloadData];
return;
}
NSInteger counter = 0;
for(NSString *name in appDelegate.globalArray )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSRange r = [name rangeOfString:searchText];
if(r.location != NSNotFound)
{
if(r.location== 0)//that is we are checking only the start of the names.
{
[tableData addObject:name];
}
}
counter++;
[pool release];
}
[myTableView reloadData];
}
You can see the code "for(NSString *name in appDelegate.globalArray )" it don't work because I fill the table view with elements of arrays inside this global array, I make an example
In a row of my table view there is a uitableviewcell and inside it there are four label;
I write these label with string of this globalArray but in this way:
[cell.label1 setText:[[appDelegate.globalArray objectAtIndex:indexPath.row]objectAtIndex:1]];
[cell.label2 setText:[[appDelegate.globalArray objectAtIndex:indexPath.row]objectAtIndex:2]];
[cell.label3 setText:[[appDelegate.globalArray objectAtIndex:indexPath.row]objectAtIndex:3]];
[cell.label4 setText:[[appDelegate.globalArray objectAtIndex:indexPath.row]objectAtIndex:4]];
then in delegate method for searchbar the code "for(NSString *name in appDelegate.globalArray )" don't work, How can I change my code?
** I DON'T SAY THAT I WANT TO CHECK ONLY LABEL1 FOR THE SEARCH
The problem here is that globalArray is an array of arrays. So the loop should be something like.
for(NSArray *rowArray in appDelegate.globalArray )
{
for ( NSString *name in rowArray ) {
// Do your processing here..
}
}
Using tableData
In your tableView:cellForRowAtIndexPath:, do this
[cell.label1 setText:[[tableData objectAtIndex:indexPath.row]objectAtIndex:1]];
[cell.label2 setText:[[tableData objectAtIndex:indexPath.row]objectAtIndex:2]];
[cell.label3 setText:[[tableData objectAtIndex:indexPath.row]objectAtIndex:3]];
[cell.label4 setText:[[tableData objectAtIndex:indexPath.row]objectAtIndex:4]];
In your numberOfSectionsInTableView:, return 1;
In your tableView:numberOfRowsInSection:, return [tableData count];
You should include a method that will reset the search data to the original content when the user stops searching.
- (void)resetSearchData {
// Get appDelegate first.
self.tableData = [NSArray arrayWithArray:appDelegate.globalArray];
}
Related
how to hide table header for null object when uisearchbar type? Here is my coding. Currently, header height does not hide when type something in UISearchbar even put [_tableview reloadData] in this function.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
self.currentResponder = searchBar;
NSMutableArray *tempClientArray = [[NSMutableArray alloc]init];
NSMutableArray *tempCoyArray = [[NSMutableArray alloc]init];
....
....
[_clientListGroupTableView reloadData];
}
Create a BOOL to detect if you are searching or not and reload table
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if (isFromSearchResults) {
return 0; //set height 0, if it is from search results or whatever your condition
}
else{
return 30; // set height for Header
}}
it may help you
I want trigger the keyDown Event with every Key action. Therefore I have created a subclass of NSView.
#interface CodrTextView : NSView {
NSTextField *lineNumberSpace;
NSTextView *mainTextView;
}
- (id) initWithTextView:(NSTextView *)textView lineNumberSpace:(NSTextField *)textField;
I already have the method's:
- (id) initWithTextView:(NSTextView *)textView lineNumberSpace:(NSTextField *)textField {
self = [self init];
if (self){
mainTextView = textView;
lineNumberSpace = textField;
}
return self;
}
- (BOOL) acceptsFirstResponder {
return YES;
}
- (BOOL)canBecomeKeyView {
return YES;
}
My plan is to count the lines in the textView and write the numbers in the lineNumberSpace. I know that the methods work, because I have test it already with an IBAction on a button. This is the method:
- (long) getLineCount{
NSString *content = [mainTextView string];
NSUInteger numberOfLines, index, contentLength = [content length];
for (index = 0, numberOfLines = 0; index < contentLength; numberOfLines++){
index = NSMaxRange([content lineRangeForRange:NSMakeRange(index, 0)]);
}
NSLayoutManager *layoutManager = [mainTextView layoutManager];
NSUInteger numberOfGlyphs =[layoutManager numberOfGlyphs];
NSRange lineRange;
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
(void) [layoutManager lineFragmentRectForGlyphAtIndex:index
effectiveRange:&lineRange];
index = NSMaxRange(lineRange);
}
numberOfLines++;
return numberOfLines;
}
- (void)keyDown:(NSEvent *)event {
NSMutableString *lineNumberString = [NSMutableString string];
long numberOfLines = [self getLineCount];
for (int i = 1; i <= numberOfLines; i++){
[lineNumberString appendString:([NSString stringWithFormat:#"%d \n", i])];
}
[lineNumberSpace setStringValue:lineNumberString];
}
This method works surely. The problem is, with a button the number in the lineNumberSpace is changing correctly but with the keyDown Event it don't work. What is my mistake here?
Ahhh, looking at your code I now realize what the problem is.
Your "CodrTextView" object is not subclassed from "NSTextView" (and also, when you instantiate the object programatically or via your XIB or Storyboard, make sure the text view object is a custom class of "CodrTextView" and not just another "NSTextView"), so it's not actually getting the "acceptsFirstResponder" or "canBecomeKeyView" method calls either.
You need to descend "CodrTextView" from "NSTextView" instead of "NSView", OR you need to create another subclassed "NSTextView" object which will receive the "keyDown:" event and then it'll call the code that calculates the string that goes into "lineNumberSpace" of your main view.
Does this make sense to you now?
I've got an NSTableView. While editing, if I hit tab it automatically jumps me to the next column. This is fantastic, but when I'm editing the field in the last column and I hit tab, I'd like focus to jump to the first column of the NEXT row.
Any suggestions?
Thanks to Michael for the starting code, it was very close to what ended up working! Here is the final code that I used, hope it will be helpful to someone else:
- (void) textDidEndEditing: (NSNotification *) notification {
NSInteger editedColumn = [self editedColumn];
NSInteger editedRow = [self editedRow];
NSInteger lastColumn = [[self tableColumns] count] - 1;
NSDictionary *userInfo = [notification userInfo];
int textMovement = [[userInfo valueForKey:#"NSTextMovement"] intValue];
[super textDidEndEditing: notification];
if ( (editedColumn == lastColumn)
&& (textMovement == NSTabTextMovement)
&& editedRow < ([self numberOfRows] - 1)
)
{
// the tab key was hit while in the last column,
// so go to the left most cell in the next row
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:(editedRow+1)] byExtendingSelection:NO];
[self editColumn: 0 row: (editedRow + 1) withEvent: nil select: YES];
}
}
You can do this without subclassing by implementing control:textView:doCommandBySelector:
-(BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
if(commandSelector == #selector(insertTab:) ) {
// Do your thing
return YES;
} else {
return NO;
}
}
(The NSTableViewDelegate implements the NSControlTextEditingDelegate protocol, which is where this method is defined)
This method responds to the actual keypress, so you're not constrained to the textDidEndEditing method, which only works for text cells.
Subclass UITableView and add code to catch the textDidEndEditing call.
You can then decide what to do based on something like this:
- (void) textDidEndEditing: (NSNotification *) notification
{
NSDictionary *userInfo = [notification userInfo];
int textMovement = [[userInfo valueForKey:#"NSTextMovement"] intValue];
if ([self selectedColumn] == ([[self tableColumns] count] - 1))
(textMovement == NSTabTextMovement)
{
// the tab key was hit while in the last column,
// so go to the left most cell in the next row
[yourTableView editColumn: 0 row: ([self selectedRow] + 1) withEvent: nil select: YES];
}
[super textDidEndEditing: notification];
[[self window] makeFirstResponder:self];
} // textDidEndEditing
This code isn't tested... no warranties... etc. And you might need to move that [super textDidEndEditing:] call for the tab-in-the-right-most-cell case. But hopefully this will help you to the finish line. Let me know!
I have an NSTreeController bound an NSArrayController bound to an entity and the tree bound to an NSOutlineView. Now, when an "add" button is clicked, I would like to add a new entity to the treeController, select the entity, and highlight it for editing. If I call [arrayController add], the insertion is asynchronous and I have no way of knowing which the new object is since the outline view does not select new rows automatically. So I am left with inserting the new Entity programatically. So addButton calls createNewGroup on a outlineViewController (see below).
Again, inserting a new entity does not seem to be a synchronous process. I can't locate it in the NSOutlineView in the next line after currentObject = [NSEntityDescription.... And I did try after reloading the data. So I am left with observing the value changes in the array controller. This sort of works, most of the time, but occasionally it does not. Is this the right approach to this sort of thing?
- (void) createNewGroup:(id)sender {
NSInteger row = [myOutlineView selectedRow];
if(row == -1) {
[groupsController addObserver:self
forKeyPath:IR_GROUPS_KEYPATH
options:NSKeyValueObservingOptionInitial
context:IR_GROUPS_CONTEXT];
currentObject = [NSEntityDescription insertNewObjectForEntityForName:#"Group"
inManagedObjectContext:appDelegate.managedObjectContext];
return;
}
if([myOutlineView levelForRow:row] != 0) return;
[subGroupsController addObserver:self
forKeyPath:IR_GROUPS_KEYPATH
options:NSKeyValueObservingOptionInitial
context:IR_SUBGROUPS_CONTEXT];
NSManagedObject *parent = [[myOutlineView itemAtRow:row] representedObject];
currentObject = [NSEntityDescription insertNewObjectForEntityForName:#"Group"
inManagedObjectContext:appDelegate.managedObjectContext];
[currentObject setValue:parent forKey:#"parent"];
}
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if([keyPath isEqualToString:IR_GROUPS_KEYPATH]) {
if(currentObject == nil) return;
[myOutlineView noteNumberOfRowsChanged];
NSString *ctx = (NSString *) context;
if([ctx isEqualToString:IR_GROUPS_CONTEXT]) {
NSInteger length = [myOutlineView numberOfRows];
NSInteger index;
for(index = 0; index < length; index++) {
id item = [myOutlineView itemAtRow:index];
if(currentObject == [item representedObject]) {
// We found the new object:
NSIndexSet *indices = [NSIndexSet indexSetWithIndex:index];
[myOutlineView selectRowIndexes:indices byExtendingSelection:NO];
[myOutlineView editColumn:0 row:index withEvent:nil select:YES];
currentObject = nil;
return;
}
}
//[groupsController removeObserver:self forKeyPath:nil];
} else if([ctx isEqualToString:IR_SUBGROUPS_CONTEXT]) {
NSTreeNode *parent = [myOutlineView itemAtRow:[myOutlineView selectedRow]];
[myOutlineView expandItem:parent];
NSInteger length = [myOutlineView numberOfRows];
NSInteger index;
for(index = 0; index < length; index++) {
id item = [myOutlineView itemAtRow:index];
if(currentObject == [item representedObject]) {
NSIndexSet *indices = [NSIndexSet indexSetWithIndex:index];
[myOutlineView selectRowIndexes:indices byExtendingSelection:NO];
[myOutlineView editColumn:0 row:index withEvent:nil select:YES];
currentObject = nil;
return;
}
}
}
}
}
If you use (a subclass of) NSTreeController to provide the content for you outlineView, it is very simple. You create a button, either in code or in Interface Builder, and set bind the target to insert: to add an element or remove: to delete it. In code it would look like this:
[aButton bind:NSTargetBinding
toObject:aController
withKeyPath:keyPathToTreeController
options:[NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSConditionallySetsEnabledBindingOption,
#"insert:", NSSelectorNameBindingOption,
nil]];
Selecting the new object is handled by the treeController. Again, in code:
[aTreeController setSelectsInsertedObjects:YES];
In IB, it's a checkbox that you need to check. Oh, there's also addChild:. Let the bindings do their magic.
When I hit the "+" button on a row in NSRuleEditor, a new row is created. How can I take influence on the criteria used for that row.
It seems NSRuleEditor defaults to selecting the first criterion sequentially from the list of possible values. I would much rather have the new row match the row where the "+" was clicked.
I was able to fake it by subclassing a private method:
- (void)_addOptionFromSlice:(id)slice ofRowType:(unsigned int)type
{
int rowIndex = [(NSRuleEditorViewSlice*)slice rowIndex];
NSArray *criteriaForRow = [self criteriaForRow:rowIndex];
NSArray *displayValuesForRow = [self displayValuesForRow:rowIndex];
self.template = [NSArray arrayWithObjects:criteriaForRow, displayValuesForRow, nil];
[super _addOptionFromSlice:slice ofRowType:type];
}
- (void)insertRowAtIndex:(NSInteger)rowIndex withType:(NSRuleEditorRowType)rowType asSubrowOfRow:(NSInteger)parentRow animate:(BOOL)shouldAnimate
{
[super insertRowAtIndex:rowIndex withType:rowType asSubrowOfRow:parentRow animate:shouldAnimate];
NSArray *template = self.template;
if (template != nil) {
[self setCriteria:[template objectAtIndex:0] andDisplayValues:[template objectAtIndex:1] forRowAtIndex:rowIndex];
}
}