Customizing table cell in ABPeoplePickerNavigationController - iphone

I've spent some time searching for this answer on SO, but couldn't find it, so here goes:
I'm starting with an ABPeoplePickerNavigationController, and when a user taps a person, they'll be taken to an ABPersonViewController where they'll be able to select phone numbers and email addresses. After they're finished with the ABPersonViewController, they'll be taken back to the ABPeoplePickerNavigationController. Pretty simple stuff.
What I want is to add a detailLabel to the table cell they selected in ABPeoplePickerNavigationController after they chose a phone number or an email address. Something like "Email and phone number chosen" or "Phone number chosen".
Apple's documentation says:
You should not need to subclass these controllers; the expected way to modify their behavior is by your implementation of their delegate.
The delegate methods provided won't handle this. Is there any way to accomplish this without subclassing myself? And, if I do have to subclass ABPeoplePickerNavigationController, which method would I override to update the detailLabel?
Thanks!

This bit of code seems to work for me, it grabs the cell when the user selects a person and adds a check mark. I'm guessing you can tweak the cell in other ways at this point as well.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
UIView *view = peoplePicker.topViewController.view;
UITableView *tableView = nil;
for(UIView *uv in view.subviews)
{
if([uv isKindOfClass:[UITableView class]])
{
tableView = (UITableView*)uv;
break;
}
}
if(tableView != nil)
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:[tableView indexPathForSelectedRow]];
if(cell.accessoryType == UITableViewCellAccessoryNone){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
[cell setSelected:NO animated:YES];
}
return NO;
}

I have idea of how to do it, i think it will be helpful to you , but never implemented like this.
First you have to go with custom table . For that table you can give all the contact names from your addressbook. you can use http://developer.apple.com/library/mac/#documentation/userexperience/Reference/AddressBook/Classes/ABAddressBook_Class/Reference/Reference.html
just go through it you can understand .
you have to use these methods.
1) - (NSArray *)people
you will get all people records into returned array. each record will have unique id , you have to retrieve it
ABRecord rec = [returnedArray objectAtIndex:0];
NSString *pid = rec.uniqueId
-(NSString *) uniqueId ( this is ABRecord property method )
once you got it you can retireve from your array what you want by using that recordid/ unique id .

Related

Working with big numbers

I have a number like 12345678910111213 and I need to pass it from one method(cellForRow) to another(button action method). The simplest way which I used to use is to pass it through a button tag. In this case it is impossible(?). I can also create a property for it but what about encapsulation? I want to know really RIGHT(and preferably simple) way for doing things like that. Thanks in advance!
Well you can actually attach the value to the UIButton. When you have the value you want to pass and you have a reference to the button:
static char kMyObject;
objc_setAssociatedObject(myButton, &kMyObject, [NSNumber numberWithInt:myInt], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
On the other side, when you receive the action with the button as id:
- (void)myAction:(id)sender
{
UIButton *myButton = (UIButton*)sender;
NSNumber *number=objec_getAssociatedOject(myButton,&kMyObject);
}
You cannot pass it as a tag as Saad said. You can use NSDecimal numbers here.
#Saad cannot use double, as it will lose precision.
In this integer tag you can store a pointer (case of 32 bit address) to a class/structure/whatever what represents bigint.
For example:
UIButton *button = [UIButton ...];
button.tag = (int)[[MyBigInt alloc] initWithString:#"12131312312312312"];
after:
MyBigInt *bigInt = (MyBigInt *)button.tag;
...
[bigInt release];
I'm going to make some assumptions here, because I just when through something similar.
The UIButton with the action is in a UITableViewCell.
You have an underlying source for all your data (ie. An array with all your data in it).
You have easy access to your tableView.
First, you need to get the cell which contains the button:
UITableViewCell *cell = nil;
for (UIView *view = sender; view; view = view.superview) {
if ([view isKindOfClass:[UITableViewCell class]]) {
cell = (UITableViewCell *)view;
break;
}
}
Next, you need to get the indexRow for that cell:
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Finally, you have to get access to your data:
ModelClass modelObject* obj = [self.data objectAtIndex:indexPath.row];
Now, you can make any changes you need to your model.

UITextField artifacts

I have an iPhone app which has a Table View-based data input screen with a toggle, which when on shows all rows in another section of the table.
Sometimes, when the app is first loaded, and usually when it has been fully deleted from the phone, the UITextFields from the original section of the table are displayed in addition to the new rows, as below (main table section is above)
:
THe strangest thing about this is that this behaviour only occurs the first time this screen is displayed - it seems fine after that. Oh, and it only seems to occur on the phone, not the simulator.
Given the random nature of this, could it have something to do with other apps? I did have apps with similar namespaces running at the same time. The problem seemed to go away after I closed the apps down / deleted from phone.
I have included the code block that is run when the switch is changed below:
- (void)accountSwitchChanged {
[customerDetails saveField:#"addToAccount" WithBool:addToAccountSwitch.on];
NSArray *indexes = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:1 inSection:1], [NSIndexPath indexPathForRow:2 inSection:1], [NSIndexPath indexPathForRow:3 inSection:1],nil];
if (addToAccountSwitch.on)
[detailsTable insertRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationFade];
else
[detailsTable deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationFade];
}
Any ideas?
--EDIT
cellForRowAtIndexPath code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get rid of grey background for iPad app
[tableView setBackgroundView:nil];
UITableViewCell *cell = nil;
NSDictionary *cellData = [[dataSourceArray objectAtIndex: indexPath.section] objectAtIndex:indexPath.row];
id cellControl = [cellData objectForKey:kCellControlKey];
static NSString *kCellControl = #"CellControl";
cell = [tableView dequeueReusableCellWithIdentifier:kCellControl];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellControl] autorelease];
cell.textLabel.textColor = [[[ConfigManager sharedInstance].skin valueForKey:#"tableCellHeadingTextColour"] toUIColor];
cell.backgroundColor = [[[ConfigManager sharedInstance].skin valueForKey:#"tableCellBgColour"] toUIColor];
cell.textLabel.font = [UIFont boldSystemFontOfSize:17];
} else {
// a cell is being recycled, remove the old control (if it contains one of our tagged edit fields)
UIView *viewToCheck = nil;
viewToCheck = [cell.contentView viewWithTag:kDetailsViewTag];
if (!viewToCheck)
[viewToCheck removeFromSuperview];
}
// if control is not a text field, make it a button
if (![cellControl isKindOfClass:[UITextField class]])
cell.selectionStyle = UITableViewCellSelectionStyleGray;
else
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = [cellData objectForKey:kCellTitleKey];
[cell.contentView addSubview:cellControl];
return cell;
}
Something to check and eliminate If it is only occurring the first time view is displayed, it's always worth checking whether you are assuming the view is loaded before you are doing work on it. This doesn't come up very often, but when it does it can be very hard to track down. To check whether this might be your issue, add a [viewController view] call just after you create the view for the first time. This forces the view to be loaded. If the issue goes away when you add this, you've tracked the source of the issue. And you can even leave the [viewController view] call in as a fix, or work through your code to allow for lazy instantiation.
All this said, far more likely to be something funny in your tableView:cellForRowAtIndexPath: code. If you want good stack overflow, post all or relevant fragment of the that code (or the relevant code it calls).
Follow up on posted code:
(1) if (!viewToCheck) [viewToCheck removeFromSuperview] won't do anything. It'll only send a removeFromSuperview message when viewToCheck is nil and that won't do anything. Not sure if this will be key issue, but it's something to put right.
(2) [cell.contentView addSubview:cellControl]; - this looks problematic - it's usually best to only add subviews when you create the cell. You can hide or show in the main body of tableView:cellForRowAtIndexPath: - that would, I suspect, work for you here. With this code there's a risk here that you'll either add multiple subviews when you only want one or (which may be what's going on here) that you end up not removing a subview when a cell is recycled. If the viewToCheck code is supposed to be removing the added cellControl then, as (1), it won't do that just now because your if condition is wrong.

Event Kit Change Calendar?

Just like in the normal iPhone calendar app, I need to make a table view which has the users calendar which they can select one and then use that and save the event to that calendar.
Any help would be much appreciated, as there doesn't seem to be much info on this around.
Thanks.
You want to check out the Event Kit Programming Guide, especially the part on Creating and Editing Events Programatically.
You basically want to allocate and initialize an EKEventStore, and use the calendars property to get the list of calendars. It's an array of EKCalendar objects.
To show them in a table, you'll end up with something like:
// eventStore is an EKEventStore instance variable which was alloc/init'ed elsewhere
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [eventStore.calendars count];
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell* aCell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if( aCell == nil ) {
aCell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:#"MyCell"] autorelease];
}
EKCalendar* aCalendar = [eventStore.calendars objectAtIndex:[indexPath row]];
aCell.textLabel.text = aCalendar.title;
return aCell;
}
You could also consider using EKEventStore's defaultCalendarForNewEvents, rather than having your own UI to choose.

UISwitch and UITableViewCell iOS4

Caveat: I have looked for the answer to my question and several come close, but I'm still missing something. Here is the scenario:
I want a way to create UITableViewCells that contain UISwitches dynamically at run time, based on the data in a table (which I can do). The problem becomes connecting the switches such that I can get their value when that view is changed (navigated away, closed, etc). I have tried to use the events UIControlEventValueChanged to be notified, but have failed to specify it correctly, because it dumps when that switch is tapped. Also, there doesn't seem to be any way to uniquely identify the switch so that if all the events are handled by a single routine (ideal), I can't tell them apart.
So...
If I have a UITableView:
#interface RootViewController : UITableViewController
{
UISwitch * autoLockSwitch;
}
#property (nonatomic, retain) UISwitch * autoLockSwitch;
-(void) switchFlipState: (id) sender;
#end
// the .m file:
#implementation RootViewController
// ...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"Cell";
int row = 0;
NSString * label = nil;
TableCellDef_t * cell_def = nil;
row = indexPath.row;
cell_def = &mainMenuTableCellsDef[ row ];
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
label = (NSString *) mainMenuTableCellsDef[indexPath.row].text;
[cell.textLabel setText:(NSString *) mainMenuItemStrings[ indexPath.row ]];
if (cell_def->isSpecial) // call special func/method to add switch et al to cell.
{
(*cell_def->isSpecial)(cell ); // add switch, button, etc.
}
else
{
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
}
and this is the 'special' function:
-(void) autoLockSpecialItem :(UITableViewCell *) cell
{
autoLockSwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
[autoLockSwitch addTarget:self action:#selector(switchFlipState:) forControlEvents:UIControlEventValueChanged ];
[cell addSubview:autoLockSwitch];
cell.accessoryView = autoLockSwitch;
}
and finally:
-(void) switchFlipState: (id) sender
{
NSLog(#"FLIPPED");
}
==============================================================
Questions:
Why would it crash (bad selector) when the switch was tapped? I believe that my code follows all the example code that I have seen, but obviously something is wrong.
I cannot put a instance method into a table as a function pointer; and it doesn't seem to like a class method either. If I make it a 'C/C++' function, how do I get access to the class/instance member variables? That is, if I want to put a call to autoLockSpecialItem into a static table (or reasonable facsimile) such that I can get autoLockSwitch member variable? If I make it a class method and the autoLockSwitch var a static, will that be valid?
More simply: how do I connect the UIControlEventValueChanged to my view (I have tried and failed) and can I differentiate at runtime within the event handler which switch has changed?
Is there a better way? I cannot believe that I am the first person to have to solve this type of problem.
Apologies for the length, appreciation for attention and grateful for any and all help.
:bp:
Don't know about why your method isn't connected, but a simple way to "differentiate at runtime within the event handler which switch has changed" is to take the (id)sender given to your event handler, walk your tableview, and compare the sender to any switches, if present, in each table item. If that's too slow, a hash table connecting senders to table cells, or something like that, is a possible optimization.
If you want to use C function pointers, you need to pass the object to the function to use it to call the object's property accessor methods within the function. (Or you could assign the object to a global variable if it's clearly a singleton, but that's a very politically incorrect answer.)
First, and easy way to define your different switches would be defining their tag based on the row number. When one of the switches is tapped you can access sender.tag to get the row number this way.
Also, you should probably be adding the switch the the cells content view, not the actual cell, [cell.contentView addSubview:autoLockSwitch]. Also the frame does need to be set (note CGRectZero, cocoa will ignore the width and height but uses the x,y coords to define where you want the switch in the cell.

IPhone UITableView suppress disclosure-button when Delete Button is displayed

I have a UITableView that has a disclosure button on every row. When the table is put into edit mode and the the Deletion control is pressed ("-" sign), the Delete Button shows, however the disclosure button is not replaced, but instead just slides to the left of the delete button.
The apple UITableView guide explains the delegates for everything it seems except for the delegate that is called when the "-" sign is pressed, but before the delete button is displayed.
I would just like to suppress the disclosure indicator while the delete button is shown.
I'm guessing that I am missing something... I have set the setHidesAccessoryWhenEditing:NO
on the cells of the table so that the indicator is displayed to indicate to the user that if they select the row, they can edit it...
The behavior I am trying to copy is done in the contacts app when a contact is edited. Any help would be greatly appreciated...
Thanks, Greg
The standard way to do this is to use cell.hidesAccessoryWhenEditing = YES, and that editing is a modal action in which navigation is typically disabled.
The Contacts application actually uses custom table cells, and I wouldn't be surprised if it didn't so much use an accessory as have an image located on the cell's right edge, judging by its behaviour.
If you want to know when the delete button appears, I'd suggest that you try installing a Key-Value observer on the cell's showingDeleteConfirmation property, like so:
[cell addObserver: self forKeyPath: #"showingDeleteConfirmation"
options: NSKeyValueObservingOptionNew context: NULL];
Then you implement the observer callback method:
- (void)observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object
change: (NSDictionary *) change context: (void *) context
{
if ( [keyPath isEqualToString: #"showingDeleteConfirmation"] )
{
UITableViewCell * cell = (UITableViewCell *) object;
BOOL isShowing = [[change objectForKey: NSKeyValueChangeNewKey] boolValue];
if ( isShowing == NO )
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
else
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
I should note that I'm not sure whether the UITableViewCell class is KVO-compliant for this property, but it's got to be worth a try…
Or when you are creating your cell you can define the accessory type: cell.editingAccessoryType = UITableViewCellAccessoryNone;
The disclosure indicator is managed by tableView:accessoryTypeForRowWithIndexPath: so maybe you could change the accessory type while in editing mode.
I believe there's a tableView:accessoryButtonTappedForRowWithIndexPath:, maybe there you can note that you're going to edit mode and then change what the tableView:accessoryTypeForRowWithIndexPath: returns for each row - no accessory when in edit mode.
I would give it a try.
Here is a calling sequence (not sure if that helps) description/tutorial/examples from Apple reference about how to go about Inserting and Deleting Rows in Editing Mode.
My comment to Jim's solution didn't come across very well... Here is the version that solved the problem for me... Thanks again Jim!
- (void)observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object
change: (NSDictionary *) change context: (void *) context
{
UITableViewCell * cell = object;
if ( [keyPath isEqualToString: #"showingDeleteConfirmation"] )
{
BOOL isShowing = [[change objectForKey: NSKeyValueChangeNewKey] boolValue];
if ( !isShowing )
{
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
}