I have 2 viewControllers (NewTicket1Controller and NewTicket2Controller). View 1 has a text field named 'ticket' and view 2 wants to access that value.
Here is my code in View 2.
NewTicket1Controller *screen1 = [[NewTicket1Controller alloc] init];
NSLog(#"%#", screen1.ticket.text);
My NSLog statement above returns null for the ticket textfield value. But it's not null. If I switch back to view 1 I can see that there is a value in that field.
Can I not retrieve a variable like this?
When you instantiate a new NewTicket1Controller, all of it's properties are initialized to their default values. This does not give you a reference to any existing NewTicket1Controller objects. If you are using storyboards, you can pass it to the other view controller in prepareForSegue, or if you are not using storyboards, you can programatically pass it to the new view controller when it is created, assuming it is created from the NewTicket1Controller.
No need to initialize you view to access it. If you want to send text contained in textfield to another view..You need to store that ticket text field value into a string of other view controller..
At NewTicket2Controller take property with NSString with name ticketString and synthesize it...Then you can use that reference for storing value of ticket at NewTicket1Controller
At button action while switching to NewTicket2Controller..Put some code at NewTicket1Controller
NewTicket2Controller *screen2 = [[NewTicket2Controller alloc] initWithNibName:#"NewTicket2Controller" bundle:nil];
screen2.ticketString=self.ticket.text;//if you propertise the ticket textfield
NSLog(#"%#", screen2.ticketString);
Hope it helps you..
You just created and inited screen1. Any and all values will be nil or initialized.
A few things:
You want to try to keep the data (model) separate from your views and controller when possible.
You're going to have to provide some connection between the 2 view controllers.
One approach is to set up one as a weak link to the other.
Example for NewTicket2Controller
#property (nonatomic, weak) NewTicket1Controller *delegate;
And then when you create NewTicket2Controller you would assign self.delegate = screen1
After that you could access methods using the delegate.
(Ideally you'd setup a protocol for the delegate)
Others have given you partial answers. Let me state everything at once.
First, never try to manipulate another view controller's view objects directly. That is serious violation of the "encapsulation" principle of object oriented programming. You should treat a view controller's views as private, and add properties or methods to communicate between view controllers.
Second, you are creating a brand new instance of your view controller and expecting to be able to use that new view controller instance to alter settings in an existing view controller. This is like buying a new car that is a perfect match for your existing car, setting the radio station on that new car, and then wondering why the radio station on the old car doesn't change. They are different cars! They are different objects! They may be feature-for-feature identical, but they are different instances of the same object. If you had an identical twin, it is still a different person, right?
You need a way to get a pointer to your existing NewTicket1Controller object. How you do that depends on how your program is set up. Post some info on how you are setting up your view controllers. Are you using a navigation controller? Is NewTicket1Controller your root view controller? How are you getting from view controller 1 to view controller 2? A segue? (assuming you're using storyboards. Tell us if you're not.)
Related
Assume I have two UIViewControllers and aPlayerView (UIView Class). And I need thePlayerView in both controllers.
Is there a way I could load thePlayerView without creating two different instances of the same class. So if I update something to thePlayerView in the first controller, the second controller displays the updated view.
Now, I alloc init PlayerView in every UIViewController I need the class in.
One way is to create a property ofAppDelegate and subview it to everyUIViewController I need, but I don't know if this is the best way to go.
You should follow MVC (Model-View-Controller) pattern when building your apps.
In MVC, your PlayerView just have to display a state.
The state is stored in a Model (i.e. an NSObject with properties, these properties being the state of your game).
Then, you can instantiate as many PlayerView as you want or need. Just be sure to give them access (in the init step for instance:
- (instancetype) initWithState:(GameState *)<yourUniqueModelObject>
to your Model object so that they can display according to your game's state.
Your Model object is unique to your application and is shared across all your PlayView when needed.
Then, if you need, you can add a Controller that will handle the logic of your app/game/interface, and make the link between your Model and your View.
I am basically trying to implement a video conference functionality using opentok.
I have two view controllers.
Class A that has a grey image(to tell user is offline).
It calls setsession from class B to establish the session.
uses ClassADelegate and implements setUserOnlineImage that sets the class A grey image to green.
Class B holds a method useronline.
Has a class method sharedinstance that gives out the singleton instance of the class
viewdidload ->sets a variable type = 2;
setsession ->sets a variable type = 1;
It also has a protocol "ClassADelegate"
Protocol ClassADelegate has method setUserOnlineImage.
Has a callback method session:streamCreated: that is called when a subscriber is created and setupPublisher that publishes the video
The flow is like this.
first Class A calls the setsession from Class B to establish session.
Then when a connect button is clicked the viewdidload is called and then the setupPublisher is called, view is modified loaded and all that.
Now when a subscriber tries to connect session:streamCreated: is called. here when i try to print type value it comes as one, likewise many other variables also become nil which inturn results in just giving the audio and the video isnt seen.
where as if first session:streamCreated: is called (first video is received and then connect is clicked) the flow works fine and the print statement in session:streamCreated: correctly prints type value as 2.
Someone help me figure out whats happening.
I want to know why the type value is getting changed & various other variables become nil. This is preventing the video from showing. Am i missing something? Is any other instance is been taken(but I am using a singleton instance)?
The flow you describe doesn't follow any of the known patterns of how UIViewControllers should behave. Specifically, you shouldn't need to use a singleton instance of a view controller. I think you need to reconsider the architecture, specifically the relationship between these two view controllers.
By the way, the viewDidLoad method is called on the view controller as soon as its view property becomes available, which can be before its on the screen. If the view controller is loading its view from a storyboard or nib, viewDidLoad is called as soon as that view is ready. Otherwise if you are implementing loadView, viewDidLoad is called after that method is finished.
Can you describe what Class A and Class B are trying to accomplish? It sounds like Class A is a view controller for some type of status view that shows a user's online/offline status. Class B sounds like its the OTSessionDelegate as well as the view controller for where the publisher/subscriber views will be placed. Why are these not the same View Controller? (generally view controllers are meant to control a "screenful" of content, unless you are using View Controller Containment). If these two view controllers are not on the screen at the same time, can you use a segue to pass data between them when the transition occurs?
UPDATE:
The additional information is useful for me to give you a recommendation. The thing I'm still uncertain about is if you actually do have these 2 view controllers' views on screen at the same time. This solution should work in both cases.
Outside of a segue, one view controller should not really be calling another view controller's methods directly (so calling setsession as you described is a bad idea). You shouldn't even set one as the delegate of another. At most they should share a Model object to communicate. The OTSession can be seen as a Model object. The challenging limitation is that when using the delegation pattern, only one object (you chose Class B) can be informed of updates. Rather than using the delegation pattern, I think you should use NSNotifications. In order to accomplish this, you should "wrap" the OTSession model in your own model object, setting your own model object as the delegate. Then you can notify both controllers of interesting changes as they happen. I've created a diagram to demonstrate:
In this diagram, all the downward solid arrows are owning references. VideoConference would be your own class and it would implement the OTSessionDelegateProtocol. On initialization, the VideoConference instance would create and own an OTSession instance. When something happens that Class A or Class B need to know about (such as the remote user coming online), VideoConference can send an NSNotification, which both controllers can be observers. Here is a useful article about NSNotifications.
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.
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.
So I'm struggling with Core Data. I'm finding there are many ways to do the same thing, and that if you try to build the app using Storyboards and UIManagedDocuments you need to read all the tutorials and examples older than last year with a translation sheet. Today's question is about finding the best practice when adding a new managed object. I've seen examples done both ways:
Create the new managed object in a table view controller (after clicking +) and giving that new shining managed object to the subordinate "Add" view controller to get user input for all the object attributes. This seems simple, and the returned object is simple to understand because it contains all the individual attributes. But I've seen example code in this "Add" view controller for a "cancel" button that deletes the managed object passed in and then calls Save Context before dismissing itself. Functional, but the MVC training gnome on my shoulder is screaming at me about having this subordinate Add View delete an object and horrors directly call Save Context. The Recipe example code from Apple appears to use this method.
Send nothing to the Add view controller, and have it send back a delegate call the table view controller that returns each of the attributes as a separate passed parameter. So the return method becomes really long: controller:(UIViewController *)controller didReturnFirstName:(NSString *)firstName andLastName:(NSString *)lastName andStreetAddress:(NSString *) and... and... and.. But this is SO consistent with MVC dogma because the managed object is created back in the table view controller when it receives all the individual attributes, and the "Add" view never touches the Model(Core Data), or throws away an unused managed object when the user changes their mind.
Even with chained delegated methods, I'm still debating with myself which is a better method. Comments and ideas from those who've lived with both forms would be a welcome addition.
Thanks.
It you look at the example in Apple's tutorial, they accomplish this task by doing a number of things outlined below, in this case they have a modal view appear to input the information that is to be added to the data model:
In the modal view that appears, they create a protocol to handle either dismissing the view or saving the data and a property of type id that implements that protocol to be the delagate, this insures whatever object that is implments the required methods
In the view controller that created the modal view, they implement the protocol, including saving the object to the data model and dismissing the modal view
In the view controller that created the modal, they set the view controller that created the modal view as the modal view delegate during the seque to the modal
So, to summarize, in the modal view to collect the new data you need to:
create a protocol and property in .h and synthesize it
#protocol yourProtocol <NSObject>;
//methods that determine what happens based on what user does, it would save your core data object
#end
#property(nonatomic, weak) id<yourProtocol> delegate;
Then, in the modal view .m file you call those methods on the delegate, likely when they pick save or done, so a method for each probably as IBAction connected to a button
[self.delegate myMethod];
In the view that presented the modal view, you implement the protocol in the .h file
#interface viewController() <yourProtocol>
and finally, add your methods to the view controller that presented the modal view's .m file, this should include removing the view and saving your core data. According to Apple and other sources, the view controller that caused the popup/modal, etc... should be the one that dismisses it. Then, in the seque using the seque indentfying, set the view controller that is presented int he modal view as the modal view's delegate.
You're right, there are many approaches to take on this one.
It sounds as if you are starting with a context that may not be saved, so in order to be able to get back to your starting point I would tackle it like this:
Start by creating a new NSManagedObject to be used as a temporary object which gets passed to your "Edit" view.
If you are editing an existing object, copy the attributes from the existing object into this new temporary object (You can use a quick for loop to copy them all generically). Otherwise proceed with the newly created object.
Pass the temporary object to the "Edit" view controller and have it treat both cases the same.
a. If the user presses cancel, have either a protocol or delegate method which notifies the tableview and the tableview then simply discards the temporary object.
b. If they press save, notify the tableview and then copy the attributes from the temporary copy back to the original and delete the temporary object (if it is an edit operation), or just leave it as the newly created object if it was an "Add" operation.
This is not really an answer so feel free to ignore, but neither is any of the others (so far) a complete answer, and I feel the need to add some points without creating a new question, because I really want to get to the bottom of this too. In particular the answer should deal with
what happens when you Cancel vs Save
what if you want to use the same controller for editing an existing entity rather than creating a new one
Nicks answer deals with apple's (current) convention for passing data back, but is not a complete answer because that tutorial does not deal with core-data managed objects, and apples own samples that do use managed objects do it differently.
But on this subject, I find that the delegate convention is unwieldy in some cases and was happy to read this from a more experienced developer:
http://robsprogramknowledge.blogspot.pt/2012/05/back-segues.html
Rob details different mechanisms for that which are all equally valid, including the protocol/delegate convention (Nick's), using a data object without a protocol (so-called "shared memory") - more like some of the others suggested, and - my favourite - using a block.
A block is a nice option because, while it works like a delegate, the code remains entirely in context in the "parent" viewcontrollers source
However, the "shared memory" makes more sense because, as you say, using anything other than the data (managed) object which you've already designed to pass all of these properties around just seems silly.
The issue for me then is, what's the convention for creating these managed objects, passing them around, then either
1. saving changes
2. canceling changes
3. canceling creation of a new entity altogether
Inafzigers answer above works something like this, but why all that copying of properties? Can't I just not save my changes?
Apple' CoreDataBooks seems to deal with this by creating a child ManagedObjectContext and a new entity in that context (insertNewObjectForEntityForName) in the prepareForSegue, and in the delegate method (when it comes back):
- if Save was clicked, it saves both this new ManagedObjectContext and the parent FetchedResultsContext to store the object
- if Cancel was clicked it does nothing, effectively discarding the new managed object context, and thus, presumably, the new object
So this might be the conventional approach, except that:
There is a comment in the code of that sample (CoreDataBooks RootViewController.m) which states that the two managed contexts are not necessary, implying that you might be able to do the same with just one. Here's the full comment:
IMPORTANT: It's not necessary to use a second context for this. You could just use the existing context, which would simplify some of the code -- you wouldn't need to perform two saves, for example. This implementation, though, illustrates a pattern that may sometimes be useful (where you want to maintain a separate set of edits).
CoreDataBooks does not use a UIManagedDocument - not sure if that makes a difference
What's not clear to me is, do we really to use insertNewObjectForEntityForName to create the managed object? What about just creating the object and then only inserting it later if the user hit Save?
Also what about if we do an Edit and a Cancel - can we use the undo manager to get rid of changes?
I think the right approach would actually be to create a new, separate NSManagedObjectContext for your AddController (if a new item is to be created) or EditController (if an existing item is to be modified).
Chose A) or B) depending on your SDK:
A) You then have the choice to either save changes in that context, or discard the context.
In case of a "save", you can merge the changes into the TableController's ManagedObjectContext through Notifications (NSManagedObjectDidSaveNotification).
In case of a 'cancel", you can just discard the separate context.
B) Alternatively, if you're on OSX 10.7(+), you could use nested NSManagedObjectContexts, creating an NSManagedObjectContext in AddController (the child context), and settings it's parentContext to the TableController's NSManagedObjectContext.
In case of a "save", you'd save the child context, and the parent context.
In case of a "cancel", you'd just discard the child context.
See a detailed example of this in Apple's CoreDataBooks example http://developer.apple.com/library/ios/#samplecode/CoreDataBooks/Introduction/Intro.html