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.
Related
As I was thinking about all the incarnations of MVC in Cocoa, I figured I could make a custom class for every View in the application and fill it with a datasource and delegate - stuff primarily considered for Controller.
This way, instead of having the infamous Massive-View-Controller, I could chop off pieces of code and put them in separate files - one class for one View - along with their datasource and delegates.
Is it a good idea, or what are the downsides?
I'm afraid that your idea sounds like you will end up with a bunch of bloated views instead of a bunch of bloated controllers.
What I'd suggest is to consider the Single Responsibility Principle: an entity should have exactly one purpose or function. What's a view's function?
It's a representation, in code, of a region of the screen. That means it needs to do two things: draw to its area and register interactions with the area. Anything that's not absolutely essential to those two subtasks shouldn't be in the view class.
This is the idea of the "dumb view". It has no logic, no decisions to make. It just gets handed some data to render. And when it gets a tap or a click, it doesn't know what the input represents, or try to figure out what to do about it. It just knows the type of the interaction and tells another object.
That other object is the view's controller. A view controller's responsibility is to mediate between the view and the rest of the system. It gives the view its data. It also accepts messages from the view about input, and then reconfigures the view based on the result of those messages.
The view controller doesn't necessarily need to compute the result on its own, however. That's usually where view controllers start getting into "massive" trouble. The view controller should pick another object to help it get the new values that the interaction produces.
One possibility for this other object is the view model, in the MVVM structure. The view model is a display-focused representation of the raw data for the view. It transforms the information in the model into whatever format the view needs, and re-transforms or updates the data in response to input from the view controller.
Another idea is to split that responsibility even more finely, using a VIPER arrangement. Here the formatting of the data is handled by a "Presenter", and the transformation of the data is the job of an "Interactor".
It's possible to get into architecture astronaut territory here; blindly applying a complex structure can bite you if a view's needs are inherently very simple. But even if you choose not to formally apply one of these alternative patterns, a view controller needs other objects. You will want "controllers" with other specific jobs, that get messages from the view controller and pass data back.
The important thing is to keep in mind the original idea I mentioned: strive to make each type do one thing and do it well. That will keep your classes focused; easy to read, understand, and think about; and testable.
View doesn't compute it's data, it just displays them. However if you have custom control it can have some logic to compute it's inner data to trigger value change inside model.
Your approach is an overkill with unnecessary code.
So you are facing a problem where you need to set some values of your custom NSView or custom NSControl (e.g. NSButton title).
Some of available MVC solutions:
set the value inside controller manually and call setNeedsDisplay method inside within view on the changed property.
set the model object as the property of view. However, this introduces tight coupling but still is ok (so the view has to
know/import model class). +Include update/refresh method within view
use bindings to nsobjectcontroller. You don't need to set Class of nsobjectcontroller (that is needed only if you need additional
functionality for it like creating object automatically on add
method).
MVC pattern reminder
View has target action mechanism which is triggered in controller. Controller updates model (nothing else!). Model then has to propagate that it has changed and controller should react to it. It shouldn't react in the target action.
With bindings you skip the target action but the latter remains
You can combine 2 and 3.
If you are beginner forget about VIPER pattern. MVVM can help you to reduce size of your controllers.
How to use binding with NSObjectController:
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "Value.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSObjectController *objectController;
#property (strong) Value *value;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.value = [[Value alloc] init];
self.objectController.content = [self value];
}
#end
#interface Value : NSObject
#property NSString *value1;
#property NSString *value2;
#property NSString *value3;
#end
#import "Value.h"
#implementation Value
- (instancetype)init
{
self = [super init];
if (self) {
[self setValue1:#"Value1"];
[self setValue2:#"Value2"];
[self setValue3:#"Value3"];
}
return self;
}
#end
There is also an option to unload View Controllers with Coordinator objects. Shortly: each task in the app is managed by a Coordinator object, which manages its View Controllers.
There is the main Coordinator object, that is retained by application delegate and that retains all other Controllers. All the logic that doesn't belong in View Controller, is moved up to the Coordinator.
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 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.)
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.
I'm developing a game and inside I have a class called StageViewController.
I noticed that code inside is becoming very very long and dull.
In this class I have controller about gesture, position, animation and it's not easy use static class or singleton class to clean this class.
Is a possible solution to use others viewcontrollers inside this StageViewController to simplify the code?
Example: If in my game I should make an entry of an object that I should color, can I use another viewcontroller (with another class) to make my code inside StageViewController more simplify?
If you have any suggestion for me or link to read you can make me happy ;-)
Typically this indicates that you're storing model information in the view controller. The view controller should only keep track of how to display information. You should move the actual state of the game into model classes. These model classes inherit from NSObject, they are not view controllers.
In a well designed model-view-controller system, you should be able to run your entire game without knowing what the display looks like. Your model should be able to take inputs, update the game state, and provide outputs, regardless of how or whether that information is actually displayed. This kind of thinking improves reusability, and also reduces the complexity of your view controllers.