I've been looking around for a good pattern to implement a insert then cancel pattern when working with a UINavigationBar and UITableView.
I have I have a "insert"button in my TeamsViewController navigation bar (screenshot)
Which when I run it runs this code:
-(void)insertTeam
{
if( !detailViewController ) {
detailViewController = [[TeamDetailViewController alloc] init];
}
if( !teams ) {
teams = [NSMutableArray array];
}
Team *team = [[Team alloc] init];
[teams addObject:team];
int lastIndex = [teams count];
[detailViewController setEditingTeam:[teams objectAtIndex:lastIndex - 1]];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[self presentModalViewController:navigationController animated:YES];
}
Which is great if the user fills out all the info, but if they hit cancel on the next view, there's an empty object in my arrary.
I'm sure there's a great pattern to achieve this but I've looked at all the TableView sample codes, two different ios books, and tried googling it, but haven't found a pattern for this.
My thought is something like the following:
When user cancels, set a canceled ivar in my Team object to YES
Back in my TeamsViewController, when the view appears check the last object in my teams array and see if it's property canceled is YES, if so remove that last object.
But this doesn't seem so slick and I was figuring there was some better way to achieve this. TIA.
I would be tempted to make the TeamsViewController a delegate of the TeamDetailViewController. The delegate would implement a method such as - (void)teamCreated:(Team *)team; and it would update the array. Since there seems to be no point to having a Team in the array that's incomplete, I would have the TeamDetailViewController create the Team and pass it back in the delegate call. On a cancel, there would be no need to do anything except pop the controller.
Related
So I am implementing a custom navigation item in my view controller via the method like this
-(UINavigationItem*)navigationItem{
item = [[SearchNavigationItem alloc] init];
item.delegate = self;
return item;
}
The SearchNavigationItem will set itself up, add a UITextField and so on.
The field.delegate will have the item as the delegate.
So the issue I have is that when I try to grab the text of the field, it is nil. But when the "textfield changed" is called, I can access the field via the argument (textFieldDidChange:UITextField*) and it has the text.
Another issue, like the title, was that when I did [field resignFirstResponder] nothing happened.
Okay so I already have the answer, and I am writing this question because I could personally not find any help while fixing it.
So the issue is that navigationItem can be called multiple times and this kept creating new bars.
So the solution became, simply, this:
-(UINavigationItem*)navigationItem{
// Apparently it should be treated as a 'singleton' which I think it says
// kind of in the documentation. This comment is just to reinforce that
// it burned me to init it each time this method is called. Which is can
// be multiple times and also outside of the class itself (like when nav'ing)
if(item == nil){
item = [[SearchNavigationItem alloc] init];
item.delegate = self;
}
return item;
}
Hope this helps someone else.
I am trying to reload data in a tableview based on a users account permissions whenever they log in.
The two classes involved in this are:
mainViewController and menuViewController
Currently I am able to use
[self.tableView reloadData];
To reload the data when called within the viewWillAppear method. Which is no good for me since the user hasn't logged in when the view loads so there is no data to populate the table at this point.
I have created a method called populateTable in menuViewController.h which I am calling in the mainViewController.m file on button press using the following;
(IBAction)Reload:(id)sender {
menuViewController *mvc = [[menuViewController alloc]init];
[mvc populateTable];
}
This seems to work correctly as I have an NSLog within the populateTable method which executes. However the reloadData does not work.
Here is my populateTable method;
-(void)populateTable {
self.section1 = [NSMutableArray arrayWithObjects:#"test settings", #"test", #"test",#"Users and access",#"No-track IPs", nil];
self.section2 = [NSMutableArray arrayWithObjects:#"Rules", #"Channels",#"Goals",#"Pages", nil];
self.menu = [NSMutableArray arrayWithObjects:self.section1, self.section2, nil];
[self.tableView reloadData];
NSLog(#"Reloading data");
}
Can you guys help me out here, I have been staring at this all day and getting nowhere, thanks!
From my experience this is likely a problem with timing - the IBOutlet of self.tableView is not ready when you call reloadData on it (add an NSLog and see for yourself - it is nil when called).
To solve this, the populateTable method must be called within the UIViewController's viewDidLoad method. This guarantees that the outlets are not nil and that everything is ready for your data population.
Also, you should not instantiate your MenuViewController with [[MenuViewController alloc] init] but using the storyboard's instantiateViewControllerWithIdentifier.
Your problem is this line,
menuViewController *mvc = [[menuViewController alloc]init];
This creates a new instance of menuViewController, not the one you see on screen. You need to get a reference to the one you have, not create a new one. How you get that reference depends on how, when, and where your controllers are created.
Good evening.
I'm developing a QRcode reader and I have one question.
When I read a vcard, I want to show the contact data in a UItableview like the contact's default uitablview in iPhone.
I want to show the contact data as above:
And I want to add the option to save to.
I want to know how can I do it. I have to manually program the view or is there some easier way to do it?
Thanks so much.
CFDataRef vCardData = (CFDataRef)[vCard dataUsingEncoding:NSUTF8StringEncoding];
ABAddressBookRef addressBook = [AddressBook Instance].addressBook;
ABRecordRef defaultSource = ABAddressBookCopyDefaultSource(addressBook);
NSArray *contacts = (NSArray *)ABPersonCreatePeopleInSourceWithVCardRepresentation(defaultSource,vCardData);
CFRelease(defaultSource);
if (contacts.count) {
ABRecordRef person = [contacts objectAtIndex:0];
ABUnknownPersonViewController *unknownPersonVC = [[ABUnknownPersonViewController alloc] init];
unknownPersonVC.unknownPersonViewDelegate = self;
unknownPersonVC.allowsAddingToAddressBook = YES;
unknownPersonVC.displayedPerson = person;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:unknownPersonVC];
[unknownPersonVC release];
[self.navigationController pushViewController:unknownPersonVC animated:YES];
[navController release];
}
instead of creating custome Contact person View using table View use above code to first get all contact person record Ref and then display them one by one using
ABUnknownPersonViewController or ABPersonViewController..
may be help you..
You need to create the UITableView and implement all of its dataSource and delegate methods to draw the sections and cells. There's no free way to do this automatically, unfortunately.
You should probably check out a table view framework such as the free Sensible TableView. These frameworks automate a lot of these common tasks and will save you a lot of time.
I've searched a lot but could not find an answer and therefore decided to ask you :).
I have an application with some views. After logging in I create a UITabBarController with 3 tabs.
Now I wish to change the second tab's badge based on how many notifications the user has.
This core works when called in the viewdidload method:
NSString *badgeValue = [NSString stringWithFormat:#"%i", [cacheNotifications count]];
if([cacheNotifications count] != 0){
[[[[[self tabBarController] viewControllers] objectAtIndex: 1] tabBarItem] setBadgeValue:badgeValue];
}
However, I have a daemon running in the background that checks for notifications every 30 seconds. It would be great if I could change the badge from this daemon.
When I call something like this:
PlatformViewController *theInstance = [[PlatformViewController alloc] init];
[theInstance updateNotificationsBadge];
It does call the function but does not update the badge. With or without the performSelectorOnMainThread.
updateNotificationsBadge:
-(void) updateNotificationsBadge{
NSString *badgeValue = [NSString stringWithFormat:#"%i", [cacheNotifications count]];
NSLog(#"Length here is: %i", [cacheNotifications count]);
if([cacheNotifications count] > 0){
NSLog(#"call..");
[self performSelectorOnMainThread:#selector(setNotification:)
withObject:badgeValue
waitUntilDone:YES];
}
}
-(void)setNotification:(NSString*)badge{
NSLog(#"Called. %#", badge);
[[[[[self tabBarController] viewControllers] objectAtIndex: 1] tabBarItem] setBadgeValue:badge];
}
How could I fix this?
Thanks in advance!
EDIT:
cacheNotifications is a global variable declared in globalVars.m. It does not get reinitialized when I call a new instance.
I call the code below from daemonClass.m
PlatformViewController *theInstance = [[PlatformViewController alloc] init];
[theInstance updateNotificationsBadge];
Instead of creating a new instance for platformViewController, you need to use existing reference. When you create a new one, cacheNotification array would not be initialized and no contents in it. So it will always returns 0.
and UITabBarController is a containerViewController contains all the viewControllers. So you don't need to change the tab badgeValue from the other class. You can simply change it from any class.
and in your setNotification: method, change the badgeValue like this.
[[[[self tabBarController] tabBar] items] objectAtIndex:1] setBadgeValue:badge];
You should use the same instance of class rather creating the new one. Which destroy the previous value. I would recommend please use NSNotificationCenter to post notification when you get a badge which will implement void getter and setter method of badge in platformViewControllerclass. then no instance would be destroy.
Hope this helps
I really don't know exactly your problem, but what I think I would use a subclass of nsobject and in each viewController I'd change the badge. Something similar to this: https://stackoverflow.com/a/9736559/2000162
func setBadge(){
let viewconrollers = self.tabBarController?.viewControllers
viewconrollers![3].tabBarItem.badgeValue = "Your String value"
viewconrollers![3].tabBarItem.badgeColor = UIColor.green
}
//call setBadge() method from view controller's viewdidload method.
// select your give number it the view controllers array for providing badge on the desired tab
I have a split view where the top section of my split shows some questions, and the bottom section shows some other stuff. The problem is that I had it written to "push" to a new view every time the user selects a question. This is obviously less than ideal because the user can enter a situation where they have 15 copies (more or less, depending on how many times the user selects a question) of the same question to go back through.
I thought that a simple solution would be to set a BOOL for when a user selects a question, but as it turns out, this introduces a new bug where the user can select a question once, but if they go back they are out of luck. I'm kind of stuck here, and any guidance would be greatly appreciated.
Program flow:
First you need to understand a little about what I am trying to do. I am building a historical inquiry app that focuses on allowing teachers to support student learning of historical inquiry. As such, there are core questions as well as documents the students can analyze.
Based on the way the app has come along, JSLDetailViewController displays the core questions and JSL_QuestionInteraction displays the questions for analyzing the documents.
Relevant code snippet:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0){
if(!didSelectQuestion){
[self performSegueWithIdentifier:#"questionDisplaySegue" sender:indexPath];
didSelectQuestion = TRUE;
} else {
JSLDetailViewController *detailView = [JSLDetailViewController alloc];
detailView.telegram = indexPath.row;
[detailView setDetailItem:indexPath];
}
}else if(indexPath.section == 1){
[self performSegueWithIdentifier:#"telegramQuestionDisplaySegue" sender:indexPath];
JSL_QuestionInteraction *questionView = [[JSL_QuestionInteraction alloc] init];
questionView.managedObjectContext = self.managedObjectContext;
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"questionDisplaySegue"]){
JSLDetailViewController *detailView = (JSLDetailViewController *)segue.destinationViewController;
detailView.telegram = index.row;
[detailView setDetailItem:index];
JSLDetailViewController *controller = (JSLDetailViewController *)segue.destinationViewController;
controller.managedObjectContext = self.managedObjectContext;
} else if ([segue.identifier isEqualToString:#"telegramQuestionDisplaySegue"]){
JSL_QuestionInteraction *questionView = [[JSL_QuestionInteraction alloc] init];
questionView.managedObjectContext = self.managedObjectContext;
}
}
Please let me know if you need any additional details to understand this problem.
I don't know if any of what I write here will fix your problem because I still don't really understand your structure, but I see a couple of things wrong in your posted code.
First, when you're doing segues you shouldn't be alloc init'ing anything in code, the segue instantiates the new controllers for you. It's not clear what you're doing with detailView in didSelectRowAtIndexPath:, you do an alloc with no init -- you should never ever do an alloc without an init. If that detailView is something that's already present on screen, you should get a reference to that instance and set telegram and detailItem on that.
In the "if" clause of prepareForSegue you are assigning the segue.destinationViewController to two different local variables, detailView and controller -- they both point to the same thing, so there's no reason to have them both.
In the "else" clause, once again your alloc init'ing a controller, which you shouldn't do. You probably want to get the segue's destination view controller instead.