Help shortening my code in didSelectRowAtIndexPath into a much easier code - objective-c

Hey developers!
I built a Table View, populated it with array objects and I implemented a code when a user clicks an object inside my Table View, it loads that object's own nib file, and I am using an if statement. (just a little note, I populated my Table View with 40 objects because that's how much things I have and need), so I'll show only the first two if statements I created at the beginning of my if statement code since I have way too many if statements for all 40 objects in my Table View:
if ([[glossaryArray objectAtIndex:indexPath.row] isEqual:#"Title"]) {
Title *titleLoad = [[Title alloc] initWithNibName:#"Title" bundle:nil];
[self.navigationController pushViewController:titleLoad animated:YES];
[titleLoad release];
}
else if ([[glossaryArray objectAtIndex:indexPath.row] isEqual:#"Meta Description Tag"]) {
MetaDescriptionTag *metaDescriptionTagLoad = [[MetaDescriptionTag alloc] initWithNibName:#"MetaDescriptionTag" bundle:nil];
[self.navigationController pushViewController:metaDescriptionTagLoad animated:YES];
[metaDescriptionTagLoad release];
}
...
So that's it, I don't want to be cocky showing all of my if statements, so yep this code works, each array objects loading it's own separate nib file (40 nib file 1 for each array objects) and each of these nib files are like Web Views, Image Views, Text Fields and other objects. Call me crazy, but this is the only code I can think of right now, so hopefully someone can help me edit this code into a much easier code, thanks

If your values in glossaryArray can be mechanically transformed into the corresponding class/nib name (e.g. by removing all spaces), you could do something like this:
NSString *className = [self classNameFromString:[glossaryArray objectAtIndex:indexPath.row]];
UIViewController *viewController = [[NSClassFromString(className) alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
If they can't be mechanically transformed, you could always construct an NSDictionary mapping from one to the other, or special-case the few that are different. Or rename the classes and nibs.

Related

How do I insert UITextField in to UITableView after button is pressed

In master view application xcode generates ready app with table view and the plus button. I want to change that button to to add a new cell but not with the date as it is by default. I want to add two text fields like label->textfield, label->textfield.
In code I have this:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (GCDetailViewController *) [[self.splitViewController.viewControllers lastObject] topViewController];
}
and the function:
- (void)insertNewObject:(id)sender{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[UITextField alloc] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
Thank You
The way to think about this is model-view-controller (MVC). _objects is your model representing whatever the user thinks is in the table. Say it's a to-do list, then objects could be an array of NSObject subclass you create like TodoItem.
You would insert new TodoItems into _objects, then tell your table (the "View" in MVC) that it's model has changed. You can do that imprecisely using reloadData, or in a more targeted fashion as your code suggests, calling insertRowsAtIndexPaths - but that call must be sandwiched between tableView beginUpdates and endUpdates.
You can add textFields in code in your cellForRowAtIndexPath, or in the cell prototype in storyboard. Your table view datasource should always refer to objects... i.e. numberOfRows answers self.objects.count, cellForRowAtIndexPath gets:
TodoItem *item = [self.objects objectAtIndexPath:indexPath.row];
and uses that item's properties to initialize the textField's text. Also, incidentally, objects should be declared like this:
#property(strong,nonatomic) NSMutableArray *objects;
...and your code should refer to self.objects almost everywhere (not _objects). Initializing it on the first insert is too late, because the table needs it to be valid right-away, as soon as it's visible. Usually, a good practice is a "lazy" init replacing the synthesized getter...
- (NSMutableArray *)objects {
if (!_objects) { // this one of just a few places where you should refer directly to the _objects
_objects = [NSMutableArray array];
}
return _objects;
}
You might find using the free Sensible TableView framework really helpful here. Here is some sample code to illustrate how you'd do this using the framework:
- (void)insertNewObject:(id)sender{
SCTableViewSection *section = [self.tableViewModel sectionAtIndex:0];
[section addCell:[SCTextFieldCell cellWithText:#"Enter Text"]];
}
Comes in really handy for these types of situations.

Xcode 4: Loading a Nib in a method in a different file

sorry if this question is elementary, but I've been stuck on this bug for the past 2 days & I haven't been able to get past it. I'm using Xcode 4.3.2
I'm trying to load a nib named AController.xib in a method called "- (void) process" in the file named BController.m
To be clear, I copied ./A/AController.xib (which is a UIView), ./A/AController.m, ./A/AController.h to the directory ./B
I only mention this because I'm not sure if it matters for my question.
Currently, my flow works as flows (which could be my problem):
A view loads with a "buy" button
the user clicks the "buy" button which has an IBOutlet named "buyNow"
"buyNow" calls "buy", which then calls "process"
process then tries to load the nib with the following (option 1):
AController *blah;
for (id object in bundle){
if ([object isKindOfClass:[AController class]])
blah = (AController *) object;
}
assert(blah != nil && "blah can't be nil");
[self.view addSubView: blah];
The error I get here is "Thread 1: signal SIGABRT" in main.m
I've also tried (option 2),
AController *myView = [[AController alloc] initWithFrame:self.view.bounds];
[self.view addSubview:myView];
[AController release];
And (option 3)
AController * vc = [[AController alloc] initWithNibBundle:#"AController" bundle:nil]; [self.nc pushViewController:vc animated:NO];
I get the same error for all 3 choices. Each option was tried in the method "process". "process" is written in B.m. Can anyone offer some help so that I may figure this out? Any guidance as to why these options failed would be very helpful for my understanding and would be much appreciated. Thanks for helping a noob!
If AController is a UIView subclass, it cannot load a NIB. Verify it is in fact a controller, but from the initWithFrame and the way you are adding it to a view, it looks like it is not, or is being handled incorrectly.

Multiple objects with NIB in NSMutableArray

I have a noob-question, that might be similar to Dynamically create multiple instances of a UIView from a NIB but I'm missing the big picture.
I've got a class with NIB, called UserViewController and I need to create multiple instances, based on the number of users, store them in an array and be able to modally navigate between them.
A class with NIB, called SelectNumberOfUsersViewController contains this IBACTION-code:
users = [[NSMutableArray alloc] init];
for (int i=0; i<numberOfUsers; i++) {
user = [[UserViewController alloc] init];
user.userid = i+1;
[user doInitialization];
[users addObject:user];
}
I see that the initWithNibName of the instance user is run, but how do I address and show the UI for the first user in the users array?
I'm not sure if commands like
myView = [[[NSBundle mainBundle] loadNibNamed:#"XXX" owner:self options:nil] objectAtIndex:0];
[[self view] addSubview:searchDateView]
should be used, since the array contains entire objects of the class User with NIB and everything - or...?
If you want to initialise a viewcontroller with a nib file you should use:
user = [[UserViewController alloc] initWithNibName:#"NibName" bundle:nil];
If you want to push that view you could call:
[self.navigationController pushViewController:user animated:true];
To present it, call:
[self presentModalViewController:user animated:true];
If you just want to add the view to the current viewcontroller use:
[self.view addSubView:user.view];
But the sure to remove the previous one too.
I hope this was of any help.

Image not being set in method

I have a class with a viewDidLoad method and an artworkInfo method as follows:
-(void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[super viewDidLoad];
}
-(void)artworkInfo:(NSNumber *)pos{
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[self.image setImage:(UIImage *)[[mainDelegate.mapAnnotations objectAtIndex:0]image]];
}
the mainDelegate thing is to gain access to the appDelegate where an array is stored, but anyway, with the "[self.image setImage...]" command where it is, the image on the app does not appear, but when I copy that exact line of code into the viewDidLoad method, it shows up like it should. I know that the artworkInfo method is being called because I debugged it and it goes through, so I can't figure out why the command would not be doing anything it's current method while it will in the viewDidLoad...?
Also, here is where the method is called and this new view is loaded from another class:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
infoPage *myInfoPage = [[infoPage alloc] init];
[myInfoPage artworkInfo:position];
[info release];
OH, I see the problem. You're instantiating 2 different infoPage classes.
Change this:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
infoPage *myInfoPage = [[infoPage alloc] init];
[myInfoPage artworkInfo:position];
[info release];
to this:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
[info artworkInfo:position];
[info release];
Ok detailed answer. In order to understand why this image is not displaying properly you have to first look at how Runloops work in Objective C.
While viewDidLoad is the method that is called when a view is loaded and it is technically also called before a view is displayed and it's view objects initialized. Since presentModalViewController is an animation there is actually some threading going on in the works.
viewDidLoad gets called before the animation is created for the presentModalView. This initializes your objects. However, due to some of the inner workings of UI Kit some processes are loaded off into a thread. When they complete they run callback methods on the main UI thread.
Since presentModalViewController is a non-blocking method your artworkInfo method gets added to the mainRunLoop before the initializer form thread adds its callback methods to the main run loop. The best approach would be to have both a UIImage property of your viewController and a UIImageView.
set the value of UIImage by calling artworkInfo BEFORE the presentModalViewController method.
in your ViewDidLoad go ahead and set the value of your UIImageView
[self.imageView setImage:self.image];
Problem solved.
This seems pretty straight forward.
So you initialize your nib and try to call your method artwork before the nib is fully loaded. <-- This is not working for you.
Then you do additional initialization by overrider viewDidLoad per the doco where the nib is loaded <-- This is working for you
So the answer is, when you call setImage before your nib is loaded, then there is nothing to set the image to. When you call setImage in viewDidLoad your nib is loaded and then things should work just fine.
I hope this explains it a bit.

How to move from xib to xib

What is the better code to move from "page" to "page"?I have a questionnaire on 4 pages and I loading 4 views from 4 xibs.
I picked up 2 way of moving from xib to xib (in my case, from page to page).
Method 1:
-(IBAction) MaleTapped: (id) sender {
Page1M *ivc = [[Page1M alloc] init];
UINavigationController *nc = [[UINavigationController alloc]
initWithRootViewController:ivc];
[self presentModalViewController:nc animated:NO];
[ivc release];
[nc release];
}
Second way:
-(IBAction)GotoPage2M:(id)sender {
page2M = [ [Page2M alloc]
initWithNibName:#"Page2M" bundle:nil];
[self.view addSubview:page2M.view];}
One method uses the RootViewController method, the second just loads the subview. For my 4 pages, which is the better/cleaner/smarter way?
I would recommend using a UINavigationViewController in this way. Going several modal views deep is icky.
- (IBAction) goToNextPage:(id)sender {
UIViewController * newView = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[self.navigationController pushViewController:newView animated:YES];
[newView release];
}
The only reason I might do subviews is for the extra transition options.
I would recommend checking out Apple's sample Page Control code. It shows how to create something that pages through multiple view controllers and load them dynamically from xibs. The example just loads the same xib several times, but you could replace it with code that loads a different view controller or xib for each page.