I have a requirement to create controls dynamically at run-time based on config retrieved from server. I'm also trying to make use of MVVM pattern.
To keep example simple, lets say I have to create N number of UILabels in a View, each with its own settings (color, font, etc.) all based on config from server.
First thing that comes to mind, is to have a listUILabels property in my ViewModel, and have the ViewModel be responsible for creation of UILabel objects and setting their attributes / properties (color, font, etc).
The View would then iterate through each UILabel in viewModel.listUILabels adding each one via self.view.addSubview()
However I read in some examples, that the ViewModel should not reference UIKit, instead should just provide data, properties & enums for the View (feel free to comment on this).
To adhere to MVVM rules, how should I partition my logic, what goes inside the View and what goes inside the ViewModel in this case?
In my opinion your View class should contain the logic for creation of UILables and setting up its UI behavior based on the config settings.
ViewModel should provide the interfaces (properties) to interact with the underlying data which should update the View when data changes in your data source (model) and data source (model) should be updated when the data changes based on user interaction (if any).
Basically the ViewModel should be as independent on the View as possible. It should be a representation of data that are related and logically belong on a single page, but it should not be dependent on the way the view itself is implemented or represented. Ideally it should be possible to change the view and its layout without having to modify the ViewModel at all.
So your best course of action would be not to include a list of UILabels in the ViewModel and instead put a list of custom classes, that will store the "data" that you want to surface on the view. Because in this case you are dealing with UI related data, there should be no problem including color or font attributes as properties of these custom classes.
Then in the view itself you can observe this list and dynamically create appropriate controls (UILabels) based on the provided data.
Related
Lets consider,
I am having a view that has common structure but different styles for buttons and textviews.
I am accessing view different viewModels.
View Model 1: While accessing the view with this model 1, styles of the button and textviews should change.
View Model 2: While accessing the view with this model 2, styles of the button and textviews should change.
What approach should we use to change styles in view corresponding scenario.
The view is responsible for its style, so there should be no style specifics in the model.
I see several options:
Implement both styles in the view and have a property (e.g. an enum with two cases) in both models to select either one.
Same as 1. but now create two styling logic parts and have a plain view that applies either one of the styles.
Have a view baseclass with a subclass for each of the two styles. Then instantiate either one of these subclasses.
Main goal should always be: Keep things simple and easy to understand.
I am working on an app in which one View Controller has the responsobility of containing thre views.
Each and every one of this view has a datasource of somme measurements.
The views are in sync -> selectind one point in one view has a resemblence in other views.
User can select in various options to disply in each of the views (e.g. 3 views but 5 view options.
Here is my current aproach to take in this situation:
Create a component protocol -all views have to conform.
Component for example is a UICollecytionView with details for the selections (which is made in other components).
The component has a view property and is added dynamicaly to the container view of the view controller by the controller.
Visible view controllers are synced by a sync manager - to which the visible view is registered by the view controller.
Delegates are mostly implemented in the Component -> some events are exposed in a component delegate (user selected etc. so the sync manager can sync other views).
The view controller is responsible for capturing a delegate from user selection of measurement batches (e.g. batch from a given day in another view controller) and then loads needed data and triggers the sync manager to give the selected batch to each of the visible components.
The component is then responsible for presenting the batch (as well as loading/finding relevant points for the new batch if earlier batches had selected points)
Is this approach correct? Having the code of each component in the view controller would (and in the first approach did) clog the sense and clarity of it!
I also did read about View Controller Containment (so that if i understand correctly would mean crating a view controller for each option and handle that element inside the view controller and swap them for visible views on the screen). Then the user selection would trigger swapping a view controller responsible for a concrete selection of an option but then the sync would best be made by the parent view controller (am I correct).
Hope I am clear enough... although I am aware that the description is hazy but you should get the main idea.
Reasuming:
What approach is better? The component seem like a good reusable components that i can pretty dynamically add to other view controllers etc.
What are the possible pitfalls of each of these approaches if any?
I have a network client iOS app. There is a "controller" object that is responsible for receiving updates from the network. It should store / forward them to the various visible pages in the app. One page may need some of the data, one page may need some other part. There is overlap.
For example, a button needs to be highlighted or not based on the status of a device on the network. Buttons on different pages may need to reflect this status.
I need to determine if my various view controllers need to handle this or if the UI elements themselves can do it. In my example, I will need the UI button to react to events, probably based on it's "tag" field.
I've thought about implementing a category to "wrap" the various UI elements, but I'd like to use storyboard layout. This seems convoluted. Or, I can set tags on the UI elements and have the enclosing view controller gather all these up into a dictionary of UI elements and do the watching/updating using tags as keys. Or...?
I guess I'd like some pointers on what model may be best. I need this to be flexible and adaptable moving forward, so I'd can't have a bunch of code and IBOutlets for each UI item. I'm trying to keep everything as generic as possible so that when I need to make changes, I can add UI elements, set their tags, and let their enclosing view controllers take care of them.
Thanks
I think you should think about it this way:
Your app needs a model that represents the external state of the
world; in your case, devices on the network.
Another part of your app (probably a singleton with some
timers) is keeping track of the state of the world (probably doing
GETSs using NSURLConnection) and then updating your model.
Your ViewControllers present a view to the user, and observe the state
of your model, telling the views to change when the model does. (e.g. your viewcontroller would observe that a device in your model changed state to offline and then set a corresponding myButton.enabled=NO).
Take a look at iOS key-value observing docs to learn more about that. If you think you'll have many views observing the same aspects of the model, consider using the NSNotification center.
I'm building an app that has views that are build programmatically. That is, I am fetching data from a database that contains information on things like the number, size and placement of buttons in a view. At some point, there will be code that uses this data to instantiate new subviews and set them up. My question is, where should this code go? The view, the viewController, or somewhere else. It seems to me that this is a grey area regarding typical MVC principles. Should a view accept data, and then know how to draw itself using this data? Or perhaps, a viewController is responsible for building all the various subviews, and then simply adding them to the view.
Thoughts? Thanks.
I agree that it's a gray area. Personally, I make a decision like that based on whether it's the data that needs manipulated or the display of that data. For example, a view controller displaying a date may need to process various dates (ie, a data represented as a DMY struct vs. a date represented as a seconds count from some reference time) into a format suitable for the view, while the view itself may be only capable of receiving one particular format (ie, DMY) and is responsible for displaying that. That's the sort of line I tend to draw between the two -- displaying data (the view) vs. interpreting data (the controller).
In your example of reconfiguring a view I would probably put most of the logic into the controller since it involves interpreting the data. I would design the view to accept configuration details such as how many items to display and what sort of layout format to use (think of a UITableViewCell), but I would design the controller to interpret the data to decide how many items and what to put in the various fields within the view (like a UITableViewController).
I am trying to delete views that have a yellow shadow from my main viewcontroller.
It registers the number correctly but it doesn't delete. (It doesn't update the view I have tried to call setNeedsDisplay and all of those lines but the don't work. It only updates when you quit out of the app an reload it. It isn't in the managedobjectcontext but it stays in the view. Am I not releasing something?) If I had it so it only passed one item .. if you clicked on it to delete.. it would have worked but this isn't working with the shadows. Can you see why???
Update:
I have views that are stored in core data (pages) and I want to delete the pages when they are selected and have a yellow shadow. If I need to how to I add the view to an array or something when it adds the shadow and then finds them when it needs to delete.
-(void)trashitems{
for (NSString *itemKey in [itemViews allKeys]){
UIView<CollectionViewItemView> *itemview = [itemViews objectForKey:itemKey];
if ([itemview layer].shadowColor == [UIColor yellowColor].CGColor){
NSLog(#"remove %i",[[NSDecimalNumber decimalNumberWithString:itemKey] unsignedIntegerValue]);
if ([dataDelegate respondsToSelector:#selector(collectionView:canDeleteItemAtIndex:)]
&& [dataDelegate collectionView:self canDeleteItemAtIndex:[[NSDecimalNumber decimalNumberWithString:itemKey] unsignedIntegerValue]]
&& [dataDelegate respondsToSelector:#selector(collectionView:didDeleteItemAtIndex:)])
{
[itemViews release];
NSUInteger itemsCountBeforeDeletion = [dataDelegate countOfItemsInCollectionView:self];
[dataDelegate collectionView:self didDeleteItemAtIndex:[[NSDecimalNumber decimalNumberWithString:itemKey] unsignedIntegerValue]];
NSUInteger itemsCountAfterDeletion = [dataDelegate countOfItemsInCollectionView:self];
if (itemsCountBeforeDeletion - 1 != itemsCountAfterDeletion){
[NSException raise:#"Collection View Deletion Exception" format:#"Count of items in collection view before deletion (%u) must equal one more than count of items in collection view after deletion (%u) but did not.", itemsCountBeforeDeletion, itemsCountAfterDeletion];
}
}
}
}
}
Like Tom said, storing a view in Core Data is bizarre. To make a view disappear, it needs to be removed from the view hierarchy. The data should be separate from the view. I highly suggest reading up on the MVC (Model-View-Controller) design pattern.
You've got a serious design problem here. This simply isn't going to work and you need to start over.
The Apple API uses the Model-View-Controller design pattern. It should have been called the Model-Controller-Interface design pattern because that better captures the true relationships. The model holds the data and data-behaviors, the controller connects the model to the interface and the interface provides the data to an external observer such as human looking at a command-line/GUI, another process or a remote server process.
You say that:
I am trying to delete views that have
a yellow shadow from my main
viewcontroller.
... but you are really not. The subviews themselves display some kind of data e.g. an image while the yellow shadow conveys to the user some kind of information about the state of that data e.g. a yellow shadow indicates that the image is older than some date. What you are really trying to do (in this example) is delete images that are older than a certain date and then you want the views of the user interface to reflect that change in the data.
Now the data of the image and its state of being older than a certain date belong in the Model. The controller reads the data from the model and configures the view and subviews according to the data provided. The controller doesn't know the logic of why the view should look like it does for any piece of represented data and the views don't know about the data at all, they just know what image they will display and what color their shadow is.
When you are using Core Data, you use it to create the model layer. You don't use it create the controllers, views or to store any state information directly related to the operation of the controllers or views. Ideally, a data model should be perfectly functional regardless of what kind of interface you eventually use i.e. it should work equally well with a command-line, a GUI, a webpage or an interprocess communication. It simply doesn't know or care about anything not directly related to the data and the associated logic (e.g. images older than a certain date need to be deleted) of how the data fits together.
So, you need to figure out what is data and data-logic and put that in Core Data while keeping the details of the UI in that displays that data in the controllers and views.
I can't really tell you exactly what you need to do because I don't know what data your app uses or what its data-logic is but I do know that you need to take all information concerning the actual views and their configuration out of Core Data.