proper way to push view controller on didSelectRowAtIndexPath - ios

I am working with an application in which there is a table view controller. When the user clicks on the cell, I want to display specifics for that cell in a separate view controller. My first attempt at doing this was the following:
CellViewController * cvc = [self.storyboard instantiateViewControllerWithIdentifier:#"cellVC"];
[cvc.name setText:fName];
[cvc.homeNum setText:hNum];
[cvc.mobileNum setText:mNum];
[self.navigationController pushViewController:cvc animated:NO];
However, this produces a view controller without the updated values fName, hNum, and mNum. I guess I have 2 questions:
1) Is there a strong difference in terms of good practice for instantiateViewControllerWithIdentifier vs. PerformSegueWithIdentifier. (I'm using the former currently because I just don't like segues as much)
2) If instantiateViewControllerWithIdentifier is an okay way of doing it, why are those values not being updated? Instead the raw 'Label' just shows up
Note: I have looked at performSegueWithIdentifier vs instantiateViewControllerWithIdentifier And I believe the question is slightly different/they didn't really answer my question. They also seem to claim the former way is bad practice but I have seen other cases where people used that way, so I am confused.
Thanks

The problem that contols, like cvc.name is not initialized, because after instantiating view controller is not loaded.
Declare properties in CellViewController, like:
#property (nonatomic, strong) NSString * nameString;
and in CellViewController.m
- (void) viewDidLoad {
[name setText:_nameString];
}

Related

iOS Delegates instead of passing data through a segue

I'm trying to learn how delegates work and wrap my head around the concept. I'm finding I get some of the ideas. I understand you use it to pass data from one view controller to another, however wouldn't it work the same if I just sent data from a segue and every time the 1st view controller would appear, it would use that data?
So for example I have 2 view controllers.
1 is homeViewController and
2 is editViewController.
I have a variable titled "addressOfHome" which is a string in homeViewController(1).
In homeViewController under the method "viewDidAppear"
I also set the addressLabel = addressOfHome.
Then I just pass the data from editViewController(2) to homeViewController(1)
when the segue's destination vc is homeViewController ?
I'm terrible at explaining things so I apologize for that, but I gave it my best shot. Thanks for your time!
Delegates are mainly used to "trigger action" on an object from another one.
An object delegates a way to handle something to someone else, for example when you click on an UIAlertView button, if its delegate is set on a viewController, alertView:clickedButtonAtIndex will be executed on the VC, which can so react as it want
I'm terrible at explaining things
Haha, yes, you are !
A delegate isn't for that - a delegate is a way to over-ride the default behaviour of some feature(s) of a class, without creating your own sub-class. See this post : How does a delegate work in objective-C?
Are you trying to understand how delegates work (in which case, I don't think your example is one that requires a delegate) or are you trying to implement the functionality you describe, and think that a delegate is the way to do it? (I think you actually want a data source).

Objective-C: what design patterns are there to hook up a model with views that are selected from a property list?

I am trying to build a MVC app with Objective-C. It is supposed to be a questionnaire app. I have all the questions and multiple choice answers stored into a property list, because I have different of questionnaires that I want to be able to load with this app. The idea is that the main model will keep track which item it should read of the property list, and select the corresponding view and viewController.
So schematically I have the following problem.
The RootView shows the start menu, that selects which questionnaire you will be able to take.
The RootViewController is the first controller called by the app delegate. It is supposed to instantiate the model and show the RootView. It furthermore controls the buttons of the RootView.
The model is supposed to wrap the items of the property list into a fitting datastructure, and supply it to the view controllers that need it.
The SelectedViewController is a controller that is a template specifically made for a type of question. The question could be a multiple choice, an open question, a 3, 5 or 7 choice likert scale kind of question, anything really. The template name that these view controllers will really get is ViewController.
The SelectedView is a tailor made view to the question type and will get the same name format as all the selected view controllers.
Here are my ideas.
My initial hunch is to use the delegate pattern, and set the model as a delegate to any SelectedViewController.
I could also use the delegate pattern to the RootViewController, and let him monitor if the SelectedViewController should be destroyed (via a delegate message). In that case, I can implement a prepareForSegue in the RootViewController to the SelectedViewController.
Since it is a questionnaire from a plist I could also add a prepare for segue to
every selected viewcontroller, but that will probably be a problem,
since there are at least 15 different ways of displaying the
questions.
Apparently there is also something like Key-Value Observing, according to this question. So that's also something I could use.
I think there is a definite way to deal with this, because the design patterns in iOS are pretty wel described, so there should be a few options for this really (or only just one). At the moment I am leaning towards setting the RootViewController as a delegate to the SelectedViewController and let the RootViewController handle the model. In this way I am extending the RootViewController to also hold all common functionality that every SelectedViewController should have.
But I am really not sure if this is the way to go, because my knowledge on design patterns is limited. My question is: what is the right option to choice in this specific situation (e.g. views and view controllers selected via a .plist file)?
There is no need for a specific pattern - you can deal with accessing an instance of a model object by name, i.e. in the same exact way that you deal with making a specific view and the view controller.
Let's say you are looking to connect the QuizQuestionViewController4MC and its QuizQuestionView4MC to their model. Let's assume that the model class is called QuizQuestionModel4MC, and that it needs to be configured with an object that you get from a key #"4MC" in the plist. Since your code learns the name of the model class only at runtime, you can create an instance using reflection:
NSDictionary *dataFromPlist = ... // Someone passes this to you
NSString *identifier = dataFromPlist[#"identifier"]; // Returns #"4MC"
NSString *ctrlName = [NSString stringWithFormat:#"QuestionViewController%#", identifier];
NSString *modelClassName = [NSString stringWithFormat:#"QuizQuestionModel%#", identifier];
id model = [[NSClassFromString(modelClassName) alloc] init];
// Configure the model with the data from plist
[model setPlistData:dataFromPlist];
// The model is ready to be used - give it to the view controller
MyBaseViewController *ctrl = [storyboard – instantiateViewControllerWithIdentifier:ctrlName];
// Give the view controller its model - now it is ready to be used
[ctrl setModel:model];
Note the class of the view controller - MyBaseViewController. This is not your root view controller, it's a base class for all your specific view controllers. It is this view controller that knows about a model, but it does not know the specific subclass in the model hierarchy. Each subclass of the view controller knows about its specific model subclass, so it can access the information from the model class directly, without going through selectors or KVP.
Of course it is up to the designer of the app to "wire up" correct view controllers to the correct models. In terms of the above example, QuizQuestionViewController4MC needs to know the structure of the QuizQuestionModel4MC in order to avoid sending unrecognized selectors to an incorrect class.

iOS - Getting information from the View Controller to drawRect in the View

My question relates to Assignment 3 in CS193p.
Im having a terrible time getting drawRect in my View to receive information passed from my View Controller. Basically, my goal is to pass view-specific information (like self.view.size.width) to the controller, have it make some modifications (like result = self.view.size.width * 2), and pass result back to drawRect so that it could take the new info and draw it. Im pretty sure I have my delegation set up correctly, and really have tried a list of potential work-arounds:
make a public View #property, which I access in my controller.m via
View *newView
someResult = newView.variable
which I would then try to access from my view.m via
self.variable
use methods set in my View's #protocal, which I try to pass data via myView.dataSource someMethod:someData
But so far none of these are currently working (i.e. my self.variable would always come out to be 0, which shows that no data was passed to it). Your help much appreciated!
UPDATE:
The culprit was that, in the storyboard, I didn't control-drag the View Controller to the View, and hence the Controller was never connected... but at least now it works. ^_^
View *newView someResult = newView.variable is not valid syntax.
If newView.variable is not of type View then that is a problem.
If newView is already declared, you would access variable using int myValue = newView.variable (that is, asuming it is of int type).
An example would be:
// Somewhere in your code, you declare your object
UIView *otherView = [[UIView alloc] init];
// Somewhere else, you set a variable of your object
otherView.backgroundColor = [UIColor blackColor];
// Again, somewhere else you want to access that value
UIColor *thatColor = otherView.backgroundColor;
I hope this explains it well enough.
Is this the correct description of what you want to do? You have a view controller (presumably the calculator type view controller from the course) and you want to communicate between that view controller (whose view has the calculator buttons) and a view other than it's own?
If so, here are the steps you should take:
In your view that you want to receive information from a delegate:
Declare an #protocol with the method you want the delegate to implement. Declare a property in the header file of the view that conforms to the aforementioned delegate: #property (nonatomic, weak) delegate<NameOfTheProtocol> delegate;
In the view controller you want to act as the delegate, declare it conforms to the protocol: MyClass: UIViewController <NameOfTheProtocol>. In the implementation file of that view controller, implement the methods of the protocol. Where ever you create the view that you want to receive information, set it's delegate property to your view controller.
To pass information between them (I have no idea what information you want to pass), your protocol method might be something like: -(CGPoint)pointToDrawAtGivenPosition:(CGPoint)point
In your view that has the delegate property, you could in drawRect do something like:
CGPoint pointToDrawAt = [self.delegate pointToDrawAtGivenPosition:CGPointMake(100, 100)];
//Now draw that point or whatever.
Basically, the view is asking its delegate to make a decision about where to draw something. It passes some information to the delegate, and the delegate responds with the correct position for the view to draw at.

Ironically Pushing Blank View Controllers

this is my first post on here, though with the help of many questions and answers from members of this community, I have brought my project to near completion.
I have read multiple threads similar to what I'm asking, but the methods were completely different. No code has worked so far.
Basically (I say this because my code involves a lovely snake-like descent into a complicated mess, but applicable snippets will be put up upon request), my problem is that I'm calling
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
and it pushes my viewcontroller in the simulator and NSLogs the string I need changed beautifully, but it pushes a blank view! The code for that run makes the view controller variable a constant:
UIViewController *viewController = [[xSheetMusicViewController alloc]initWithNibName:nil bundle:nil];
So I thought to myself, what am I doing!? So I went back to the old method, which involved making the UIViewcontroller an if-then, if-else-then statement that would push different views depending on whether certain rows were selected (standard stuff). Which pushed a new view with my string loaded perfectly, but it only NSLog'ed one string over and over! And the worst part was the my app would call either SIGABRT, or EXC_BAD_ACCESS when I tried returning to the rootviewcontroller. (here's the applicable code):
UIViewController *viewController = [[[UIViewController alloc]init]autorelease];
if (indexPath.row == 0 && indexPath.section == 0) {
appDelegate.baseURL = #"mussette.pdf";
viewcontroller = [[xSheetmusicViewController alloc]initwithnibname:nil bundle:nil];
}
else if (...)
//pushview, changestring, blah blah//
Now, I would prefer that my view push the PDF like it's supposed to, and have the correct string value (and not give me SIGABRT or EXC_BAD_ACESS, but those are givens), but it seems that compromise is just out of my reach. I know there's probably something stupid I'm doing that could be solved with one line of code, but for now, it seems hopeless.
Thanks in advance.
EDIT: To answer all of your questions, yes, there is no xib, rather an
(id)init
method in the next view.
EDIT 2: to answer lostInTransit's request and add some additional details:
<else if (indexPath.row == 1 && indexPath.section == 0) {
appDelegate.baseURL = #"Importing PDF's.pdf";
Also, if it helps, the output keeps logging:
Application tried to push a nil view controller on target .
When I try to push the view from a tableviewcell, and it did that before when it loaded the PDF right so I ignored it.
Question: why do you first initialize your viewController as a UIViewController and then again as xSheetmusicViewController? I think the problem is with releasing values properly. In one init, you do an autorelease, in the other you don't. So chances are you are releasing a variable twice leading to the BAD ACCESS.
Do you mind posting the "blah blah" :) in the last piece of code?
Do you have a file named xSheetmusicViewController.xib in your application? That will be loaded with your view controller as its owner after you call [[xSheetmusicViewController alloc] initNithNibName:nil bundle:nil]; (it will actually be loaded when the view property is first accessed). If that file doesn’t exist, then the view controller’s -loadView: method will be called to load its view.
If you have a blank view, either you have a blank or mis-named nib (perhaps you renamed the class but not the nib?) or you aren’t creating the right view in -loadView:.

Is it a bad idea to set a UIViewController as a property of another UIViewController?

For example, say I have a RootViewController class and AnotherViewController class, and I need to change a property in my RootViewController from AnotherViewController... is it safe to have a "RootViewController" property in AnotherViewController.h (so that I can access its instance variables)?
#interface AnotherViewController : UIViewController {
RootViewController *rootViewController;
}
#property (nonatomic, retain) RootViewController *rootViewController;
#end
#implementation AnotherViewController
#synthesize rootViewController;
- (void)someMethod {
// set the data was added flag, so the rootViewController knows to scroll to the bottom of the tableView to show the new data
self.rootViewController.dataWasAdded = YES;
// if the user came in via a search result, make the search controller's tableView go away
self.rootViewController.searchDisplayController.active = NO;
}
If that's not a good idea, can anybody explain why?
In the code above, I know I could have used a protocol/delegate to handle the same thing - and I'm guessing I probably should. However, none of the books or other materials I've read has really discussed this.
The reason I'm asking is that I'm in the process of making my app universal, and using a UISplitViewController I've noticed that I need to often update my "master view" as the user makes changes in the "detail view". So, I took what seemed the easy route and started setting UIViewControllers as properties... but I'm experiencing some hard to track memory leaks and occasional crashes. I read something about "circular references", and wonder if that could be part of the issue (I do have a couple of places where UIViewControllers are set as properties of one another).
Thanks for any insight, or pointers to reference materials that cover this.
I'd avoid making a habit of this as there are better safer alternatives. Using a protocol/delegate is the preferred Apple way of managing data across classes. You can also set up NSNotifications to send/trigger data/events from one class to another. Key Value Observing (KVO) is also a decent way to listen in for changes.
In MVC structuring, the child views and downstream controllers really should have no idea (aka, keeping references) of their parents. It should always work the other way around where the parents manage and keep track of the children.

Resources