For background: I'm a Windows automation and data translation "expert" (or so they say grins) in my day job. I've been dabbling with Objective-C coding off and on since I bought my first Mac in 2004.
I'm working on an IOS app. My data container class knows how to save and load from disc, and each object responds to an instance method of -(void)saveToImpliedFilename{} or -(void)save:(NSString *)filename {}. There's a static call to load the data files from storage and create distinct data objects from them (they're fairly lightweight objects, so I'm not worried about loading several at a time). The app's domain is such that many of them won't ever be loaded at once anyway.
+(NSArray *)loadData {}
That's all working fine and wonderful. In storage the objects are stored as Xml and life is good.
Where I'm having trouble is when trying to modify the tutorials so that two things happen for me:
Quick note: I'm using the tutorial as a basis for POC coding, then I'll go back and start over with the "real" coding, reusing my data objects and some of the other utility I've built along the way.
Here's my list of goals and the issues:
I want the table view to tell the data objects to save at pretty much every "edit" event. The only one I can consistently get to work is reorganizing the table's order. (the save button and adding a new entry works fine)
entering a new entry into the list creates a nice modal editor with a save and a cancel button which work wonderfully. But if I edit an existing entry, I can't reproduce the save buttons' behaviors. Each time I try, the buttons' events no longer fire. I can't figure out where I'm going wrong.
I'm using the "Editable Table View" project from this tutorial series as my basis: http://www.aboutobjects.com/community/iphone_development_tutorial/tutorial.html
In the following code, the [self isModal] test is where the save/cancel buttons are made visible and wired up. Bringing up the new-entry screen is apparently the only time it's modal. I tried wiring this stuff up so that the buttons were created all the time, but again, the events never fire for either one. The next block below is where the editable table view is called explicitly with the NEW functionality, but the nonModal view of the same tableview is called by the select event on the selector table.
So...
// code snipped for the new/modal editor
- (void)viewDidLoad {
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
// If the user clicked the '+' button in the list view, we're
// creating a new entry rather than modifying an existing one, so
// we're in a modal nav controller. Modal nav controllers don't add
// a back button to the nav bar; instead we'll add Save and
// Cancel buttons.
//
if ([self isModal]) {
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self
action:#selector(save)];
[[self navigationItem] setRightBarButtonItem:saveButton];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:#selector(cancel)];
[[self navigationItem] setLeftBarButtonItem:cancelButton];
}
// do stuff here to display my object...
}
// this code is called from the selection table to explicitly add a new data object.
- (void)add {
vhAddVehicleViewController *controller = [[vhAddVehicleViewController alloc] initWithStyle:UITableViewStyleGrouped];
id vehicle = [[Vehicle alloc] init];
[controller setVehicle:vehicle];
[controller setListcontroller:self];
UINavigationController *newNavController = [[UINavigationController alloc] initWithRootViewController:controller];
[[self navigationController] presentViewController:newNavController animated:YES completion:nil];
}
// this is where it's called on the table selection to show the same view without the save/cancel buttons.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
vhAddVehicleViewController *controller = [[vhAddVehicleViewController alloc] initWithStyle:UITableViewStyleGrouped];
NSUInteger index = [indexPath row];
id vehicle = [[self vehicles] objectAtIndex:index];
[controller setVehicle:vehicle];
[controller setTitle:[vehicle Vehiclename]];
[[self navigationController] pushViewController:controller animated:YES];
}
I'm assuming the issue is that presenting it makes it modal, where as pushing it doesn't...? That's fine. But when I take out the test for modal to try to keep the buttons working, no joy. The buttons draw and click when tapped, but the events don't fire.
HALP! :-)
Thanks much.
-- Chris (I logged in with my Google account so at the top of the page I'm showing as "user1820796") shrug
You forgot to call [super viewDidLoad];
Update
Try removing the cancel button that goes on the left side when pushing the view controller. See if save starts working. I think the problem is you should not add a left button to the navigation bar when the view controller is pushed.
Which method signature are you using?
- (void)save
{
NSLog(#"Saving");
}
Or
- (void)save:(id)sender
{
NSLog(#"Saving");
}
I still think this was related to push/popping the view rather than presenting the view. I switched it all to presentation and it's working how I want now.
Thanks for the assistance guys. Quite a different paradigm than I'm used to on the GUI stuff, but I'm getting there.
thanks!
Related
I have implemented pull to refresh in a table view that is a subview to my main view like so:
UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.tableView;
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;
This updates the table and everything fine, the problem I'm having is when I touch the table to drag the cells seem to randomly shift up above the screen. This happens when I touch and drag from the middle of the screen down, or if I pull before the the cells shift back to their normal position. Here are some screen shots to better paint a picture. Keep in mind that all of these are taken after I drag down, yet the cells shift upward.
Should look like:
actually looks like these after the cells jump:
For anyone that may be having this issue or an issue like this I found a way to get the scroll to refresh to work very smoothly and not cause the issues I was having. I'm using an actual UITableViewController now instead of an embedded UITableView which helped with some of the scroll issues, but I still had an issue where instead of animating the closing of the refresh icon (table sliding up to cover the spinning circle) it just shut instantly and seemed very jarring. I put my refresh logic in the background, let it finish and then finished refreshing. My code structure is as follows:
- (void)viewDidLoad {
//set up refreshcontrol to call my refresh method
}
- (void)refresh {
[self performSelectorInBackground:#selector(getUpdatedInfo) withObject:nil];
}
Updated info runs and my TableViewController is a delegate of my class that gets refreshed information. When all the information is received and the update is completed I call the delegate method in my refreshing TableViewController class that puts the received array into the table, updates the table, and ends refreshing.
- (void)didFinishingUpdatingWithArray:(NSArray *)array {
//configure table sections
//Save the data
[self.tableview reloadData];
[self.refreshControl endRefreshing];
}
So, I have a master detail application. So far an object is created at launch and added to the master list, the detail view works fine as well.
What I would like to do, is to let the user add object to the master list by pressing a button. I don´t need a new view for that though, I want to use the standard Detail View for this part.
When the user taps the "Add" button, a new object should be created and then go into the detailed view of that object. I thought, "hey, why not use the standard segue as well?"
So I added a Bar Button Item to the MasterView.
This my code from MasterViewController.m:
-(IBAction)addNewItem:(UIStoryboardSegue *)segue{
if([[segue identifier] isEqualToString:#"showDetail"]){
CoolItem *newItem;
NSDate *today = [NSDate date];
newItem = [[CoolItem alloc]initWithDate:today];
[self.dataController addCoolItemWithItem:newItem];
DetailViewController *detailViewController = [segue destinationViewController];
detailViewController.coolItem = newItem;
}
}
"showDetail" is the identifier of the standard segue that comes with the master detail template.
The result: Nothing! The method is not called at button tap.
I´ve made sure my button is connected to this method, (it does however shows up as a unwind segue in the document outline).
Any ideas on what goes wrong?
I'm not sure what went wrong but I was able to achieve the desired functionality using the following code:
-(void)viewDidload {
...
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(Add:)];
}
-(IBAction)Add:(id)sender
{
// Create the new item and set it as active
...
[self performSegueWithIdentifier: #"SegueName" sender:self];
}
Hope this helps
Yaron
I want to force the back button of my navigationViewController to call the dissmissmodalViewController to prevent the fact that the user tap the back button to fast and the app send a message to a deallocated instance... how can I solve?
Thanks
You question feels a bit odd because the back button typically does something like:
[self.navigationController popViewControllerAnimated:YES];
I'm not sure how that is impacting any modal view controllers. If you really need to change its functionality, then you would basically hide the built in back button and replace it with your own custom one kind of like this: (put this in viewDidLoad)
[self.navigationItem setHidesBackButton:YES]; //hide the built in button
//create your new button
UIBarButtonItem *b = [[UIBarButtonItem alloc]initWithTitle:#"new-back-button" style:UIBarButtonItemStyleDone target:self action:#selector(customBackButton:)];
//set the new button
self.navigationItem.leftBarButtonItem = b;
then setup your new method to handle the button push
- (IBAction)customBackButton:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
good luck with your project.
I have an app that is controled through a UITabBar. In one of the sections within the tab bar, I have a navigation table. It works fine from an example I did from one of the books but I want to be able to go another view controller (aka another xib file) when the user selects a row and I want the user to be able to go back easily. I realize this has to do with pushingViewControllers but I am stuck. Here is where I think the problem is. my code is at the bottom. If you notice, I commented out
// [self presentModalViewController:flowerDetailViewController animated:YES];
While this did take me to the my flowerDetailViewController XIB file, I lost the ability to do navigation (go back). If anyone has any ideas or suggestions, it would be much appreciated.
Thank you
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
FlowerDetailViewController *flowerDetailViewController =
[[FlowerDetailViewController alloc] initWithNibName:
#"FlowerDetailViewController" bundle:nil];
/*flowerDetailViewController.detailURL=
[[NSURL alloc] initWithString:
[[[flowerData objectAtIndex:indexPath.section] objectAtIndex:
indexPath.row] objectForKey:#"url"]];*/
flowerDetailViewController.title=
[[[flowerData objectAtIndex:indexPath.section] objectAtIndex:
indexPath.row] objectForKey:#"name"];
//[self presentModalViewController:flowerDetailViewController animated:YES];
[self.navigationController pushViewController:
flowerDetailViewController animated:YES];
[flowerDetailViewController release];
}
It doesn't make any sense to execute these two methods on the same view consecutively.
[self presentModalViewController:flowerDetailViewController animated:YES];
..
[self.navigationController pushViewController: flowerDetailViewController animated:YES];
because they give a very different result, from user experience point of view.
The first one presents a view modaly. Modaly means, that he application brings up the view to the top (imagine a Z-axis, when you hold the phone, it is a line from the phone to you, on top of it means closer to you), and the user is stuck in that view exclusively because it is on the top, he/she cannot touch anything else from the application unless he resolves the options presented in the view and the view goes away.
The second method is pushing the view onto he stack of views that all belong to the navigation controller. The navigation controller pushes views onto the screen like you would lay a stack of cards onto the table, card1, put onto that card 2, put onto that card 3...and so on card N. But you still have the ability to touch other options that are all around the navigation controller.To get back to the card 1, you need to remove card(views) that are on top of it, for removing on-top views, the navigation controller provides the back button automatically.
Only you cann tell, which of these two is handy in terms of your application UI and design.
I have an app that's pretty much a large presentation of a companies product.
I have some additional functionality which I need to be accessible throughout my app.
The intended functionality for this toolbar is that it'll sit as a small, subtle tab along the bottom of the screen. When you tap the tab, the menu will expand upward (i.e. animate it's frame.y property), allowing you to tap any of the buttons contained within the menu.
The difficulty I'm having is that the app currently spans over several view controllers, and as I need this accessible throughout, in the interest of not duplicating code it would seem appropriate to make this ViewController also, that I would just load into my other view controllers, but this, by all accounts isn't recommended by Apple.
How can I build this menu functionality without duplicating code? I've tried a few things but cannot get the menu to display on my view.
Below, I'll show what I have at the moment.
Code that will sit on any view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
ToolboxMenu *toolboxMenu = [[ToolboxMenu alloc] init];
[self.view addSubview:toolboxMenu.view];
}
Code that builds the toolbar:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Creating View");
self.view = [[UIView alloc] initWithFrame:CGRectMake(0,0,88,209)];
self.view.backgroundColor = [UIColor clearColor];
UIImageView *toolboxImage = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"popup_toolbox" ofType:#"jpg"]]];
toolboxImage.frame = self.view.frame;
[self.view addSubview:toolboxImage];
}
In the above code, the NSLog fires when the menu is initialised, but I see no menu.
Rather than try to inject this into every view, I would recommend creating a separate UIWindow and float it over the main UIWindow. This is how the keyboard is implemented. Doing it that way will avoid any changes to any of the existing views. You will need to handle device rotation by hand for it, but that shouldn't be too difficult.
IIRC Apple's TabViewController was designed differently in that it's backwards to how you describe above. A TabViewController is the main or master ViewController with siblings being the display ViewControlers. Basically you have one TabViewController that will tab between views.
I would recommend you either redesign your view hierarchy OR extend UIViewController with a class that manages and displays the lower tab menu. Then you can easily init a single class that handles that in each of your views.